.NET 平台的前端框架,使用C#和HTML构建
ASP.NET Core Blazor 简介
★Blazor UI库
Blazor的两种方式
WebAssembly
应用于客户端,在第一次访问的时候就把预编译好的代码,下载到客户端的浏览器里面,然后就在客户端的浏览器运行了。
可以运行在所有的浏览器上面。
部署可以不用.NET。是个web服务器就行
拥有SPA体验。
老浏览器不支持。
第一次下载可能有点大。
Server
服务端的Blazor是在一个ASP.NET Core里面。客户端访问了服务端后,他们之间的通信使用SignalR进行通信。
需要下载的东西比较小。
可以使用所有服务器端的API。
有完整的Debug体验。
任何浏览器都可以运行
项目结构
ASP.NET Core Blazor 项目结构
这是用vs2019创建的blazor wasm应用,勾了ASP.NET Core的项目结构
Pages文件夹:里面放的都是Blazor的可路由的组件或者页面,和Vue组件一样 Shared文件夹: 包含共享的组件和样式表
运行方式
在wwwroot静态文件里面有个index.html,里面有个div的id是app
<body><div id="app">Loading...</div><div id="blazor-error-ui">An unhandled error has occurred.<a href="" class="reload">Reload</a><a class="dismiss">🗙</a></div><script src="_framework/blazor.webassembly.js"></script></body>
在Program.cs文件中(应用入口),把根组件App添加到index.html里面的id为app的标签里面。用根节点App替换#app里面的内容
public static async Task Main(string[] args){var builder = WebAssemblyHostBuilder.CreateDefault(args);builder.RootComponents.Add<App>("#app");builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });await builder.Build().RunAsync();}
而根节点App如下,外层就是路由组件,如果url有这个路由,就会把相应路由的组件放到Found组件里面RouteView组件里面,默认用了一个MainLayout布局页面。
当然如果没有找到这个url的路由组件,就会显示NotFound里面的东西
<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>
路由
在每个组件的最上面写路由
一个组件可以用多个路由,这些路由访问同一个组件。
@page "/"@page "/index"@page "/A"<h1>Hello, world!</h1>Welcome to your new app.<SurveyPrompt Title="How is Blazor working for you?" />
路由参数
路由可以加一个参数,用花括号括起来,C#代码需要一个属性来接收这个参数,用特性Parameter标识。如果路由不带这个参数,访问不到。
@page "/counter/{Count}"<h1>Counter</h1><p>Count: @Count</p><button class="btn btn-primary" @onclick="IncrementCount">Click me</button>@code {private int currentCount = 0;[Parameter]public string Count { get; set; }private void IncrementCount(){currentCount++;}}
不限制路由参数的都是string类型。可以用{参数名:参数类型}来限定路由参数是什么类型的
@page "/counter/{Count:int}"
URL和导航状态
通过NavigationManager类来管理Url和导航
@page "/counter/{Count:int}"@inject NavigationManager NavigationManager<h1>Counter</h1><p>Count: @Count</p><button class="btn btn-primary" @onclick="IncrementCount">Click me</button>@code {private int currentCount = 0;[Parameter]public int Count { get; set; }private void IncrementCount(){currentCount++;}protected override void OnInitialized(){//获取查询字符串,需要先注册组件//其他用法看文档var query = new Uri(NavigationManager.Uri).Query;}}
NavLink
和a标签一样,用这个标签路由到其他页面,href地址就是路由地址
<NavLink class="nav-link" href="counter"><span class="oi oi-plus" aria-hidden="true"></span> Counter</NavLink>
依赖注入
ASP.NET Core Blazor 依赖关系注入
有几个默认注册的类,看文档。
自己注册在Program类里面注册,使用注册的服务,在组件页面上面@inject 注册服务类型 使用服务时的属性名
组件
ASP.NET Core 的 Razor 语法参考
ASP.NET Core Razor 组件
组件是使用 C# 和 HTML 标记的组合在 Razor 组件文件(文件扩展名为 .razor)中实现的
组件名称必须以大写字母开头
代码分离
组件里面有C#的代码,把分离到单独的类文件
这是原本组件的代码
@page "/counter"<h1>Counter</h1><p>Current count: @currentCount</p><button class="btn btn-primary" @onclick="IncrementCount">Click me</button>@code {private int currentCount = 0;private void IncrementCount(){currentCount++;}}
在当前组件文件夹里面新建一个类,和组件同名,后缀加上.cs
把code里面代码拷贝进去,这是个部分类
using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;namespace Blog6.Client.Pages{public partial class Counter{private int currentCount = 0;private void IncrementCount(){currentCount++;}}}
现在.razor页面里面就不用写C#代码了,全部写到单独的部分类里面
组件传参
组件之间可以互相嵌套。
这是定义了一个子组件
<h3>@Name</h3><h3>@Age</h3><h3>@Score</h3>@code {[Parameter]public string Name { get; set; }[Parameter]public int Age { get; set; }[Parameter]public double Score { get; set; }}
在父组件中调用子组件,给子组件传参
@page "/"<h1>Hello, world!</h1>Welcome to your new app.@*在当前组件用子组件*@@*这是第一种属性传参*@<New Name="张三" Age="12" Score="98.5"/>@*第二种传多个参数用字典的形式*@<New @attributes="kv"/>@code{//public Dictionary<string, object> kv { get; set; } = new Dictionary<string, object>(){{ "Name","李四" },{"Age",14 },{"Score",59.5 }};}
子组件方法在父组件里执行
就是子组件用回调函数
在父组件里面嵌套子组件,这个子组件就是一个输入框和按钮,输入框绑定了值。 子组件声明了一个回调函数属性,就把他看成一个属性。 在父组件给这个子组件的属性OnSearch传参数,只不过这个参数是一个在父组件定义的方法。 现在这个方法传给了子组件的OnSearch属性。 当点击“搜索”按钮时,子组件执行方法SearchText,但是这个方法里面没有具体的方法实现,OnSearch属性是一个方法,我在SearchText里面调用OnSearch方法。而OnSearch的方法是父组件传过来,具体实现在父组件定义。 最后点击了按钮,执行的函数是父组件的SSS
这是子组件
<input type="text" @bind-value="@Value" /><a @onclick="SearchText">搜索</a>@code{public string Value { get; set; }//这是个回调函数[Parameter]public EventCallback<string> OnSearch { get; set; }public void SearchText(){OnSearch.InvokeAsync(Value);}}
这是父组件
@page "/"<h1>Hello, world!</h1>Welcome to your new app.<Search OnSearch="SSS" />@code{public void SSS(string text){//最终执行的回调}}
生命周期
ASP.NET Core Razor 组件生命周期
Blazor的生命周期与React组件的生命周期类似,也分为三个阶段:初始化阶段、运行中阶段和销毁阶段,其相关方法有10个,包括设置参数前、初始化、设置参数之后、组件渲染后以及组件的销毁,但是这些方法有些是重复的,只不过是同步与异步的区别
| 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
比如这样
布局
默认布局
当没有为组件指定特定布局时,路由到这个组件,就会用App的默认布局
<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><AntContainer />
自定义布局
在布局文件夹里面新建一个.razor文件。里面写布局页面的框架。
最上方写@inherits LayoutComponentBase表示继承自LayoutComponentBase ,说明这是个母版页,使@Body作为占位,组件就显示在这里。
@inherits LayoutComponentBase<div class="page"><div class="sidebar"><NavMenu /></div><div class="main"><div class="top-row px-4"><a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a></div><div class="content px-4">@Body</div></div></div>
然后在需要自定义布局页的组件上面写@layout 模板页名,这样路由到这个页面就会使用这个自定义的布局页面
@page "/"@layout 模板页名<h1>Hello, world!</h1>Welcome to your new app.
