.NET 平台的前端框架,使用C#和HTML构建
ASP.NET Core Blazor 简介

★Blazor UI库

Ant Design Blazor

Blazor的两种方式

WebAssembly

应用于客户端,在第一次访问的时候就把预编译好的代码,下载到客户端的浏览器里面,然后就在客户端的浏览器运行了。
可以运行在所有的浏览器上面。
部署可以不用.NET。是个web服务器就行
拥有SPA体验。
老浏览器不支持。
第一次下载可能有点大。

Server

服务端的Blazor是在一个ASP.NET Core里面。客户端访问了服务端后,他们之间的通信使用SignalR进行通信。
需要下载的东西比较小。
可以使用所有服务器端的API。
有完整的Debug体验。
任何浏览器都可以运行

项目结构

ASP.NET Core Blazor 项目结构
这是用vs2019创建的blazor wasm应用,勾了ASP.NET Core的项目结构
image.png

Pages文件夹:里面放的都是Blazor的可路由的组件或者页面,和Vue组件一样 Shared文件夹: 包含共享的组件和样式表

运行方式

在wwwroot静态文件里面有个index.html,里面有个div的id是app

  1. <body>
  2. <div id="app">Loading...</div>
  3. <div id="blazor-error-ui">
  4. An unhandled error has occurred.
  5. <a href="" class="reload">Reload</a>
  6. <a class="dismiss">🗙</a>
  7. </div>
  8. <script src="_framework/blazor.webassembly.js"></script>
  9. </body>

在Program.cs文件中(应用入口),把根组件App添加到index.html里面的id为app的标签里面。用根节点App替换#app里面的内容

  1. public static async Task Main(string[] args)
  2. {
  3. var builder = WebAssemblyHostBuilder.CreateDefault(args);
  4. builder.RootComponents.Add<App>("#app");
  5. builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
  6. await builder.Build().RunAsync();
  7. }

而根节点App如下,外层就是路由组件,如果url有这个路由,就会把相应路由的组件放到Found组件里面RouteView组件里面,默认用了一个MainLayout布局页面。
当然如果没有找到这个url的路由组件,就会显示NotFound里面的东西

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

路由

在每个组件的最上面写路由
一个组件可以用多个路由,这些路由访问同一个组件。

  1. @page "/"
  2. @page "/index"
  3. @page "/A"
  4. <h1>Hello, world!</h1>
  5. Welcome to your new app.
  6. <SurveyPrompt Title="How is Blazor working for you?" />

路由参数

路由可以加一个参数,用花括号括起来,C#代码需要一个属性来接收这个参数,用特性Parameter标识。如果路由不带这个参数,访问不到。

  1. @page "/counter/{Count}"
  2. <h1>Counter</h1>
  3. <p>Count: @Count</p>
  4. <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
  5. @code {
  6. private int currentCount = 0;
  7. [Parameter]
  8. public string Count { get; set; }
  9. private void IncrementCount()
  10. {
  11. currentCount++;
  12. }
  13. }

不限制路由参数的都是string类型。可以用{参数名:参数类型}来限定路由参数是什么类型的

  1. @page "/counter/{Count:int}"

URL和导航状态

通过NavigationManager类来管理Url和导航

  1. @page "/counter/{Count:int}"
  2. @inject NavigationManager NavigationManager
  3. <h1>Counter</h1>
  4. <p>Count: @Count</p>
  5. <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
  6. @code {
  7. private int currentCount = 0;
  8. [Parameter]
  9. public int Count { get; set; }
  10. private void IncrementCount()
  11. {
  12. currentCount++;
  13. }
  14. protected override void OnInitialized()
  15. {
  16. //获取查询字符串,需要先注册组件
  17. //其他用法看文档
  18. var query = new Uri(NavigationManager.Uri).Query;
  19. }
  20. }

NavLink

和a标签一样,用这个标签路由到其他页面,href地址就是路由地址

  1. <NavLink class="nav-link" href="counter">
  2. <span class="oi oi-plus" aria-hidden="true"></span> Counter
  3. </NavLink>

依赖注入

ASP.NET Core Blazor 依赖关系注入
有几个默认注册的类,看文档。
自己注册在Program类里面注册,使用注册的服务,在组件页面上面@inject 注册服务类型 使用服务时的属性名

组件

ASP.NET Core 的 Razor 语法参考
ASP.NET Core Razor 组件
组件是使用 C# 和 HTML 标记的组合在 Razor 组件文件(文件扩展名为 .razor)中实现的
组件名称必须以大写字母开头
image.png

代码分离

组件里面有C#的代码,把分离到单独的类文件
这是原本组件的代码

  1. @page "/counter"
  2. <h1>Counter</h1>
  3. <p>Current count: @currentCount</p>
  4. <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
  5. @code {
  6. private int currentCount = 0;
  7. private void IncrementCount()
  8. {
  9. currentCount++;
  10. }
  11. }

在当前组件文件夹里面新建一个类,和组件同名,后缀加上.cs
image.png
把code里面代码拷贝进去,这是个部分类

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. namespace Blog6.Client.Pages
  6. {
  7. public partial class Counter
  8. {
  9. private int currentCount = 0;
  10. private void IncrementCount()
  11. {
  12. currentCount++;
  13. }
  14. }
  15. }

现在.razor页面里面就不用写C#代码了,全部写到单独的部分类里面

组件传参

组件之间可以互相嵌套。
这是定义了一个子组件

  1. <h3>@Name</h3>
  2. <h3>@Age</h3>
  3. <h3>@Score</h3>
  4. @code {
  5. [Parameter]
  6. public string Name { get; set; }
  7. [Parameter]
  8. public int Age { get; set; }
  9. [Parameter]
  10. public double Score { get; set; }
  11. }

在父组件中调用子组件,给子组件传参

  1. @page "/"
  2. <h1>Hello, world!</h1>
  3. Welcome to your new app.
  4. @*在当前组件用子组件*@
  5. @*这是第一种属性传参*@
  6. <New Name="张三" Age="12" Score="98.5"/>
  7. @*第二种传多个参数用字典的形式*@
  8. <New @attributes="kv"/>
  9. @code{
  10. //
  11. public Dictionary<string, object> kv { get; set; } = new Dictionary<string, object>()
  12. {
  13. { "Name","李四" },
  14. {"Age",14 },
  15. {"Score",59.5 }
  16. };
  17. }

子组件方法在父组件里执行

就是子组件用回调函数

在父组件里面嵌套子组件,这个子组件就是一个输入框和按钮,输入框绑定了值。 子组件声明了一个回调函数属性,就把他看成一个属性。 在父组件给这个子组件的属性OnSearch传参数,只不过这个参数是一个在父组件定义的方法。 现在这个方法传给了子组件的OnSearch属性。 当点击“搜索”按钮时,子组件执行方法SearchText,但是这个方法里面没有具体的方法实现,OnSearch属性是一个方法,我在SearchText里面调用OnSearch方法。而OnSearch的方法是父组件传过来,具体实现在父组件定义。 最后点击了按钮,执行的函数是父组件的SSS

这是子组件

  1. <input type="text" @bind-value="@Value" />
  2. <a @onclick="SearchText">搜索</a>
  3. @code
  4. {
  5. public string Value { get; set; }
  6. //这是个回调函数
  7. [Parameter]
  8. public EventCallback<string> OnSearch { get; set; }
  9. public void SearchText()
  10. {
  11. OnSearch.InvokeAsync(Value);
  12. }
  13. }

这是父组件

  1. @page "/"
  2. <h1>Hello, world!</h1>
  3. Welcome to your new app.
  4. <Search OnSearch="SSS" />
  5. @code
  6. {
  7. public void SSS(string text)
  8. {
  9. //最终执行的回调
  10. }
  11. }

生命周期

ASP.NET Core Razor 组件生命周期
Blazor的生命周期与React组件的生命周期类似,也分为三个阶段:初始化阶段、运行中阶段和销毁阶段,其相关方法有10个,包括设置参数前、初始化、设置参数之后、组件渲染后以及组件的销毁,但是这些方法有些是重复的,只不过是同步与异步的区别
1997712-20200405180006291-1493915010.jpg

1 每次参数注入时或在父级被修改时调用。即从父级传过来的参数,或者祖先级传过来的级联参数,或者路由参数。在初始化之前,接受一个ParameterView类型参数 SetParametersAsync
2 初始化,参数注入完成后 OnInitialized/OnInitializedAsync
3 每次参数设置之后调用,完成之后开始渲染组件,如果是异步则等待完成后渲染 OnParametersSet/OnParametersSetAsync
4 每次渲染结束后调用,接受一个参数firstRender表示是否是首次渲染,只有在首次渲染是是true,其余均是false。 OnAfterRender/OnAfterRenderAsync
5 除了首次,每次渲染完成后调用,返回一个bool值,表示是否进行渲染 ShouldRender
6 组件销毁前调用 Dispose
7 状态更改时调用 StateHasChanged

CSS隔离

给每个组件设置CSS,和部分类一样,组件全名加个.css
比如这样
image.png

布局

默认布局

当没有为组件指定特定布局时,路由到这个组件,就会用App的默认布局

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

自定义布局

在布局文件夹里面新建一个.razor文件。里面写布局页面的框架。
最上方写@inherits LayoutComponentBase表示继承自LayoutComponentBase ,说明这是个母版页,使@Body作为占位,组件就显示在这里。

  1. @inherits LayoutComponentBase
  2. <div class="page">
  3. <div class="sidebar">
  4. <NavMenu />
  5. </div>
  6. <div class="main">
  7. <div class="top-row px-4">
  8. <a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
  9. </div>
  10. <div class="content px-4">
  11. @Body
  12. </div>
  13. </div>
  14. </div>

然后在需要自定义布局页的组件上面写@layout 模板页名,这样路由到这个页面就会使用这个自定义的布局页面

  1. @page "/"
  2. @layout 模板页名
  3. <h1>Hello, world!</h1>
  4. Welcome to your new app.