.NET8 Blazor 从入门到精通:(一)关键概念
最近在学习 Blazor ,在B站上找了一个国外的课程边看边学习。嗯,原价¥1503的课程,大概200多美元,课程链接如下:
B站(大章节分P-适合初学):.NET 8 Blazor 从入门到精通
B站(小章节分P-适合复习):Blazor从入门到精通(中文字幕)
官网课程:Blazor From Start to Finish

Blazor 的关键概念
本文主要介绍Blazor 的关键概念,每个知识点都附上了学习过程中查到的参考资料。文中删除了一些常识性或表述不清的内容,如热重载、组件与页面等。
项目模板
项目开发的常用模板配置项如下,其它配置也可以都试一下,观察一下区别:

Auto 交互方式:最初使用 Blazor Server,并在随后访问时使用 WebAssembly 自动进行交互式客户端呈现,详细内容参考.NET8 Blazor的Auto渲染模式的初体验。
Razor 语法
参考 ASP.NET Core 的 Razor 语法参考,前期主要理解下面几个重点语法即可:
- 隐式 Razor 表达式:以 @ 开头,后跟 C# 代码
<p>@DateTime.Now</p>
<p>@DateTime.IsLeapYear(2016)</p>
- 显式 Razor 表达式:由 @ 符号和圆括号组成
<p>Last week this time: @(DateTime.Now - TimeSpan.FromDays(7))</p>
- @code 块:允许 Razor 组件将 C# 成员(字段、属性和方法)添加到组件
@code {
// C# members (fields, properties, and methods)
}
- 循环语句和条件语句:如 @for、@if 等,直接写在页面中
@for (var i = 0; i < people.Length; i++)
{
var person = people[i];
<p>Name: @person.Name</p>
<p>Age: @person.Age</p>
}
@if (value % 2 == 0)
{
<p>The value was even.</p>
}
依赖注入
参考 将依赖项注入 Blazor 组件
在 Program.cs(项目引导程序) 中注册依赖项:
builder.Services.AddSingleton<DemoDependency>();
//用于注册依赖项的其他模式...
对于 Blazor 组件,有两种方法可以指示我们的组件使用哪些依赖项:
//1.在 Razor 标记中
@inject IToDoApi ToDoApi
@inject ISomeServiceType AnotherService
//2.在 C# 代码中
@code
{
[Inject]
private IYetAnotherServiceType PropertyInjectedDependency { get; set; }
}
注入配置
参考 ASP.NET Core Blazor 配置 ,其中配置的优先级别:用户机密 > appsettings.{Environment}.json > appsettings.json 。
在 appsettings.json 中配置连接字符串:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Default": "连接字符串来自appsettings.json"
}
}
在组件中引入配置依赖:
@page "/"
@inject IConfiguration config
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
<h2>@config.GetConnectionString("Default")</h2>
IConfiguration 是默认注册的,不需要另外写代码注册,可以直接使用。
HeadOutlet 组件
切换页面时不是整个页面被重新加载,实际上只有根组件 App.razor 的 <Routes /> 被重新渲染。这种渲染方式不利于SEO,可以使用 HeadOutlet 组件来控制 <head> 元素的内容来进行优化。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="KeyConcepts.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
</head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
参考 在 ASP.NET Core Blazor 应用中控制 <head> 内容,指定一个页面的标题和描述:
@page "/control-head-content"
<PageTitle>@title</PageTitle>
<p>Title: @title</p>
<p>Description: @description</p>
<HeadContent>
<meta name="description" content="@description">
</HeadContent>
@code {
private string description = "This description is set by the component.";
private string title = "Control <head> Content";
}
- 使用 PageTitle 组件指定页面标题,这样可以将 HTML <title> 元素呈现给 HeadOutlet 组件。
- 使用 HeadContent 组件指定 <head> 元素内容,该组件为 HeadOutlet 组件提供内容。
需要注意,如果在A页面用了B页面,那么B页面的 PageTitle 会覆盖掉A页面的 PageTitle。所以,组件不需要作为页面使用时就不要放 PageTitle 了。
@code 分离
Blazor可以支持在razor文件里面添加cs代码,但是代码一旦复杂了之后就会变得特别的麻烦。其实,这部分代码在编译时实际是被分离出来的,我们也可以在编译前手动将它们分离出来。
右键 code ,选择 快速操作和重构 ,然后如下图所示选择 将块提取到代码隐藏中:

结果如下,其中①只是②的一个快捷方式:

上面是使用VS的自动分离功能,也可以使用手动的方式进行分离。参考 C# Blazor 学习笔记(4):blazor代码分离,注意以下几点:
- 直接右键razor组件的上级目录,添加一个partial局部类
- 新建类的类名是xxx.razor.cs,这样才能挂到组件上面
xxx.razor
xxx.razor.cs:代码
xxx.razor.css:css样式
代码分离后,依赖项也需要改成属性注入:
using Microsoft.AspNetCore.Components;
namespace KeyConcepts.Client.Pages;
public partial class Demo
{
// 在razor组件中是这样的 @inject IConfiguration config
[Inject]
protected IConfiguration config { get; set; }=default!;
private string? GetConnectionString()
{
return config.GetConnectionString("Default");
}
}
Blazor 调试
调试没什么好说的,就在VS中正常打断点、单步运行、监控变量值就行了,具体参考 调试 ASP.NET Core 应用。
CSS 隔离
CSS 隔离可以将 CSS 范围限定到 Razor 组件,以简化 CSS 并避免与其他组件或库发生冲突,但过多的使用也会导致 CSS 追踪困难。
参考 ASP.NET Core Blazor CSS 隔离 ,在与组件相同文件夹中创建一个 .razor.css 文件,该文件与组件的 .razor 文件的名称相匹配。例如为 Counter.razor 组件创建一个 Counter.razor.css 文件:
h1 {
color:red;
}
生成时 Blazor 会重写 CSS 选择器以匹配组件呈现的标记, 重写的 CSS 样式被作为静态资产捆绑和生成, 默认情况下在 标记中引用表样式:
<!-- {ASSEMBLY NAME} 占位符是项目的程序集名称 !-->
<link href="{ASSEMBLY NAME}.styles.css" rel="stylesheet">
在捆绑的文件中,每个组件都与范围标识符关联。 对于每个具有样式的组件,HTML 属性追加有格式 b-{STRING},其中 {STRING} 占位符是框架生成的十个字符的字符串。 标识符对每个应用都是唯一的。
在呈现的 Counter 组件中,Blazor 将范围标识符追加到 h1 元素:
<h1 b-zdeg3nv67a="">Counter</h1>

注:如果CSS不生效,需要清理一下浏览器的缓存。
调用JavaScript
js文件可以放到wwwroot目录下,也可以关联到特定组件,参考
从与组件并置的外部 JavaScript 文件 (.js) 加载脚本 为 Counter 组件添加并置js文件:
//Counter.razor.js
export function displayCount(count) {
alert('The count is' + count);
}
export function createMessage(count) {
return 'The count is' + count;
}
Blazor 应用的 Razor 组件使用 .razor.js 扩展名并置 JS 文件(参考 CSS 隔离部分),并且可通过项目中文件的路径公开寻址 {PATH}/{COMPONENT}.razor.js :
- 占位符 {PATH} 是指向组件的路径
- 占位符 {COMPONENT} 是组件
修改 Counter 组件的代码,调用js函数:
@page "/counter"
@rendermode InteractiveAuto
@inject IJSRuntime JSRuntime
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<h2>@subMessage</h2>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private string subMessage = "";
private IJSObjectReference? jsModule;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
jsModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./Pages/Counter.razor.js");
}
}
private async Task IncrementCount()
{
currentCount++;
await jsModule.InvokeVoidAsync("displayCount", currentCount);
subMessage = await jsModule.InvokeAsync<string>("createMessage", currentCount);
}
}
- @inject IJSRuntime JSRuntime:注入 IJSRuntime 接口,用于与客户端 JavaScript 交互
- IJSObjectReference? jsModule:保存对 JavaScript 模块的引用
- JSRuntime.InvokeAsync
<IJSObjectReference>:加载 JavaScript 模块并保存其引用
实际项目中,尽量不要使用js控制DOM,而是使用Blazor组件,因为两者可能起冲突。
.NET8 Blazor 从入门到精通:(一)关键概念的更多相关文章
- 【UML】NO.70.EBook.9.UML.4.001-【PowerDesigner 16 从入门到精通】- 基础概念
1.0.0 Summary Tittle:[UML]NO.70.EBook.9.UML.4.001-[PowerDesigner 16 从入门到精通]- 基础概念 Style:DesignPatte ...
- 20、ASP.NET MVC入门到精通——WebAPI
本系列目录:ASP.NET MVC4入门到精通系列目录汇总 微软有了Webservice和WCF,为什么还要有WebAPI? 用过WCF的人应该都清楚,面对那一大堆复杂的配置文件,有时候一出问题,真的 ...
- Nginx开发从入门到精通 学习目录分享学习 (阿里著作)
Nginx开发从入门到精通 缘起 nginx由于出色的性能,在世界范围内受到了越来越多人的关注,在淘宝内部它更是被广泛的使用,众多的开发以及运维同学都迫切的想要了解nginx模块的开发以及它的内部 ...
- CUDA从入门到精通
http://blog.csdn.net/augusdi/article/details/12833235 CUDA从入门到精通(零):写在前面 在老板的要求下.本博主从2012年上高性能计算课程開始 ...
- Java入门-浅析Java学习从入门到精通【转】
一. JDK (Java Development Kit) JDK是整个Java的核心,包括了Java运行环境(Java Runtime Envirnment),一堆Java工具和Java基础的类库 ...
- (升级版)Spark从入门到精通(Scala编程、案例实战、高级特性、Spark内核源码剖析、Hadoop高端)
本课程主要讲解目前大数据领域最热门.最火爆.最有前景的技术——Spark.在本课程中,会从浅入深,基于大量案例实战,深度剖析和讲解Spark,并且会包含完全从企业真实复杂业务需求中抽取出的案例实战.课 ...
- 【转载】google搜索从入门到精通
原文地址:http://www.cnblogs.com/helloIT/articles/5095668.html /***************************************** ...
- 2017最新技术java高级架构、千万高并发、分布式集群、架构师入门到精通视频教程
* { font-family: "Microsoft YaHei" !important } h1 { color: #FF0 } 15套java架构师.集群.高可用.高可扩展. ...
- Android 学习资料入门到精通(PDF集合)共54本
最近收集一些安卓入门到精通,包含游戏编程,网络编程,多媒体开发,需要学习朋友就下载保持下来,下载链接在最下面 下面是网盘内容 14天学会安卓开发_(完整版).pdf Android 4 游戏高级编程 ...
- SAP从入门到精通 知识体系
初步认识 SAP标准课程培训 详细信息点击: SAP从入门到精通课程 标准培训主要是基于SAP标准的课程架构,定期在SAP的培训中心面向广大SAP客户开设公开课.培训过程中会应用SAP标准的教材内容, ...
随机推荐
- 霍夫变换原理及实现(Opencv C++)
已知一幅图像中的n个点,假设我们希望找到这些点中位于直线上的子集.一种可能的解决方法是,首先找到由每对点确定的所有直线,然后寻找靠近特定直线的那些点的所有子集.这种方法涉及寻找n(n-1)/2~n2条 ...
- .NET FRAMEWORK Entity Framework,EF 手动DB FIRST,不使用设计器
环境: VS2019 SQL SERVER 2012 一.在数据库(db1)创建表结构 "db1"是数据库名,"Table1"是表名. USE [db1] GO ...
- word文档生成视频,自动配音、背景音乐、自动字幕,另类创作工具
简介 不同于别的视频创作工具,这个工具创作视频只需要在word文档中打字,插入图片即可.完事后就能获得一个带有配音.字幕.背景音乐.视频特效滤镜的优美作品. 这种不要门槛,没有技术难度的视频创作工具, ...
- Python3.7+Robot Framework+RIDE1.7.4.1安装使用教程
一.解惑:Robot Framewprk今天我们聊一聊,Robot Framework被众多测试工程师误会多年的秘密.今天我们一起来揭秘一下,最近经常在各大群里听到许多同行,在拿Robot Frame ...
- injectionIII iOS代码注入工具(下)
injectionIII iOS代码注入工具(下) 本文将解决如何使用injectionIII对主页热重载,如果对injectionIII不了解的同学请回到上篇查看 Vaccine 简单地说Vacci ...
- 打开ftp服务器上的文件夹时发生错误,请检查是否有权限访问该文件夹
产生这种现象有很多方面的原因 如果你能用命令行的方式访问ftp服务器,但是不能用资源管理器访问,那么请看下去. 1.打开IE浏览器,点击设置 2.点击Internet选项,进入高级 3.取消勾选 大功 ...
- Linux-swap管理
什么是Swap Swap:交换空间,即虚拟内存,用于解决内存不足的问题. 在内存不足的时候,会将内存中不常使用的部分存入硬盘,使得内存得以腾出空间供新的程序使用. 那么,虚拟内存即为硬盘中的部分. S ...
- Linux 内核:设备树(2)dtb转换成device_node
Linux 内核:设备树(2)dtb转换成device_node 背景 前面我们了解到dtb的内存分布以后(dtb格式),接下来就来看看内核是如何把设备树解析成所需的device_node. 原文(有 ...
- Chrome插件:Postman Interceptor 调试的终极利器
今天给大家介绍一款非常实用的工具--Postman Interceptor. 这个工具可以捕捉任何网站的请求,并将其发送到Postman客户端. 对于经常和API打交道的程序员来说,Postman I ...
- 单qubit量子门
量子编程的基本单元就是量子门.量子编程有点像传统的电路设计,一个量子程序可以被写成量子门序列. 图中有一些符合,比如H门.X门.Z门.测量等,我们都会接触到. 传统计算机程序的输入和输出可以不一样,但 ...