Blazor第1天_Blazor Server跟Blazor WebAssembly專案種類與技術差異介紹

 



本次筆記學習皆以vs2019 .net 5以上版本做示範
會分別紀錄vs2019 .net5 與 vs2022 .net6的專案開發方式

如果專案本身有一些要跟.net framework以前的4.5左右相依
電腦空間有限
建議可以採用vs2019 與.net5的配套
https://docs.microsoft.com/zh-tw/visualstudio/releases/2019/compatibility





因為vs2022(才支援到.net6)會只支援到4.6(含)以上的版本
https://docs.microsoft.com/zh-tw/visualstudio/releases/2022/compatibility


Blazor的介紹

Blazor 其實就是Browser 加上以前的Razor語法 (Browser + Razor)
屬於一種基於.net core的 Single Page Application(SPA) 框架,因此可跨平台
其實就是微軟有個野心想讓C#這個程式語言可以不只限於用在後端也可以將前端涵蓋。

Blazor主要包含以下幾項特色
Component-based 架構
Javascript Interop
Forms & Validation
Routing
State Management
Layouts


而Blazor主要又分為兩種Hosting Model
1.Blazor Server
2.Blazor WebAssembly

所謂Hosting Model
也就是所謂的Render這些
監聽UI變化的相應計算機制的Blazor component邏輯的模式


Blazor Server跟Blazor WebAssembly技術差異

Blazor Server技術介紹

主要會需要一個server,在Server上運行完並即時渲染到Browser的模式。
Blazor Server是一套底層藉由SignalR來當作Client Side與Server Side的溝通媒介
一些js dom或event trigger都會藉此技術來做交互。
(備註:SignalR 技術基本上就是一套底層透過websocket達到real-time的連線更動open source library)





Blazor Server 優點:

下載檔案較小

對於.net相關工具較有可運用性

Server運用性較多,程式也在Server端上。


Blazor Server 缺點:

有較高的延遲

不好Scalable

不支援Offline時候的運行

不可採用Serverless佈署


Blazor WebAssembly(WASM)技術介紹

WebAssembly
其實又可被縮減為WASM
是一種可被運行在modern web browser的low-level assembly-like language(二進位制指令)
底層技術上就是將被編譯過的程式轉譯為WASM最終下載到client-side的browser去運行
(所有程式經過編譯後的dll跟其他相關運行資源都會被下載到Browser運行)




撇除IE基本上比較新一點的Modern Web Browser都有支援




在Blazor WASM 應用中又有再針對開發導向是否有包含.net core後端
細分兩種型態
1.Standalone:不含asp.net core 後端
2.Hosted:含asp.net core 後端

Blazor WASM 優點:
不具有.net server-side相依性

對於Server Loading會較少

對於Client資源利用最大化,支援Offline時候的部分邏輯運行。

可採用Serverless佈署


Blazor WASM 缺點

網頁第一次Load會要Load可能比較久
(由於會把很多後端編譯好的dll跟程式檔下載到Client Browser,所以下載量較大。)

針對Browser具有是否支援的強依賴(如上述Browser支援)




專案新建方式


Blazor Server專案新建







基本上跟之前.net core mvc 或 .net core web api本身專案架構學習方式一樣
程式進入點
一定就是從Program.cs的Main開始
~\BlazorServerApp1\Program.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BlazorServerApp1
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}


在Startup.cs程式中則是服務相關的注入和一些middleware配置
~\BlazorServerApp1\Startup.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using BlazorServerApp1.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace BlazorServerApp1
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddSingleton<WeatherForecastService>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            app.UseStaticFiles();

            app.UseRouting();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");
            });
        }
    }
}


在第51行的
endpoints.MapBlazorHub();
就是去建立SignalR與BlazorServerSide的連線溝通


在第52行的
endpoints.MapFallbackToPage("/_Host");


則是將所有Http 請求導向至
~\BlazorServerApp1\Pages\_Host.cshtml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@page "/"
@namespace BlazorServerApp1.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
    Layout = null;
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>BlazorServerApp1</title>
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="BlazorServerApp1.styles.css" rel="stylesheet" />
</head>
<body>
    <component type="typeof(App)" render-mode="ServerPrerendered" />

    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>

    <script src="_framework/blazor.server.js"></script>
</body>
</html>

第33行引入的就是Blazor Server引入有關SignalR的js

第20行
<component type="typeof(App)" render-mode="ServerPrerendered" />

代表預設會注入一個叫做App的Component
~\BlazorServerApp1\App.razor

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

採用ServerPreRendered的模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#region 組件 Microsoft.AspNetCore.Mvc.ViewFeatures, Version=5.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref\5.0.0\ref\net5.0\Microsoft.AspNetCore.Mvc.ViewFeatures.dll
#endregion

namespace Microsoft.AspNetCore.Mvc.Rendering
{
    //
    // 摘要:
    //     Describes the render mode of the component.
    //
    // 備註:
    //     The rendering mode determines how the component gets rendered on the page. It
    //     configures whether the component is prerendered into the page or not and whether
    //     it simply renders static HTML on the page or if it includes the necessary information
    //     to bootstrap a Blazor application from the user agent.
    public enum RenderMode
    {
        //
        // 摘要:
        //     Renders the component into static HTML.
        Static = 1,
        //
        // 摘要:
        //     Renders a marker for a Blazor server-side application. This doesn't include any
        //     output from the component. When the user-agent starts, it uses this marker to
        //     bootstrap a blazor application.
        Server = 2,
        //
        // 摘要:
        //     Renders the component into static HTML and includes a marker for a Blazor server-side
        //     application. When the user-agent starts, it uses this marker to bootstrap a blazor
        //     application.
        ServerPrerendered = 3,
        //
        // 摘要:
        //     Renders a marker for a Blazor webassembly application. This doesn't include any
        //     output from the component. When the user-agent starts, it uses this marker to
        //     bootstrap a blazor client-side application.
        WebAssembly = 4,
        //
        // 摘要:
        //     Renders the component into static HTML and includes a marker for a Blazor webassembly
        //     application. When the user-agent starts, it uses this marker to bootstrap a blazor
        //     client-side application.
        WebAssemblyPrerendered = 5
    }
}



在App 元件又有引入了一些MainLayout元件
~\BlazorServerApp1\Shared\MainLayout.razor

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@inherits LayoutComponentBase

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <div class="main">
        <div class="top-row px-4">
            <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
        </div>

        <div class="content px-4">
            @Body
        </div>
    </div>
</div>

MainLayout元件裡面定義側欄Menu的元件


~\BlazorServerApp1\Shared\NavMenu.razor


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">BlazorServerApp1</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </li>
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

這裡每個<NavLink class="nav-link" href="xxxx"></NavLink>
也就是App.razor當中會傳遞的route資料,藉此來換頁。
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />








留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header