IT评测·应用市场-qidao123.com

标题: C#逆袭前端:Blazor WebAssembly 全栈开辟揭秘 [打印本页]

作者: 冬雨财经    时间: 2025-3-24 08:57
标题: C#逆袭前端:Blazor WebAssembly 全栈开辟揭秘
一、引言

在当今的 Web 开辟范畴,技术的快速迭代与创新令人目不暇接。传统的前端开辟主要依赖 JavaScript 语言,共同各种框架如 React、Vue 和 Angular 等,来构建交互性强、体验丰富的用户界面。这些技术栈在已往的十几年间极大地推动了 Web 应用的发展,从简单的网页展示进化到现在功能复杂、媲美桌面应用的 Web 应用步伐。
然而,随着.NET 技术的不停演进,特殊是.NET Core 的跨平台特性以及对 Web 开辟的深度支持,一种全新的前端开辟方式 ——Blazor WebAssembly 应运而生。它打破了传统前端开辟对 JavaScript 的依赖,允许开辟者使用 C# 语言进行前端开辟,为 Web 开辟带来了新的思绪和解决方案。
C# 作为一种强类型、面向对象的编程语言,拥有丰富的类库和强大的开辟工具支持,在后端开辟范畴已经取得了巨大的成功。Blazor WebAssembly 则将 C# 的能力拓展到了前端,让开辟者可以或许在全栈开辟中使用同一种语言,镌汰了因语言切换带来的学习成本和开辟成本。同时,它使用 WebAssembly 技术,将.NET 代码编译成二进制格式在欣赏器中运行,实现了高性能和接近原生的用户体验。
对于广大 C# 开辟者而言,Blazor WebAssembly 无疑是一个令人高兴的技术。它不但提供了一种全新的前端开辟方式,还让他们可以或许在熟悉的编程环境中完成前后端的开辟工作。在接下来的内容中,我们将深入探讨 Blazor WebAssembly 的技术原理、开辟流程以及实际应用场景,一起明白 C# 在前端开辟中的魅力 。
二、Blazor WebAssembly 是什么

二、Blazor WebAssembly 是什么

(一)界说与原理

Blazor WebAssembly 是微软推出的一个基于 WebAssembly 的当代 Web 应用步伐框架,它允许开辟者使用 C# 和.NET 技术构建客户端 Web 应用步伐,而无需使用 JavaScript。在传统的 Web 开辟中,前端主要依赖 JavaScript 来实现各种交互逻辑和功能。而 Blazor WebAssembly 打破了这一通例,它将 C# 代码编译为 WebAssembly 字节码,然后在欣赏器中运行。
WebAssembly 是一种新型的二进制格式,它可以在当代欣赏器中以接近原生的性能运行。简单来说,WebAssembly 就像是一个翻译器,它可以或许将高级编程语言(如 C#、C++、Rust 等)编写的代码转换为一种高效的、可以在欣赏器中直接执行的格式。对于 Blazor WebAssembly 而言,它借助 WebAssembly 的能力,让 C# 代码可以或许在欣赏器环境中运行,从而实现了用 C# 进行前端开辟的可能。
例如,我们可以编写一个简单的 C# 方法,在 Blazor WebAssembly 应用中实现一个计数器功能:
  1. private int count = 0;
  2. private void IncrementCount()
  3. {
  4.     count++;
  5. }
复制代码
在上述代码中,IncrementCount方法用于增加计数器的值。这段 C# 代码会被编译为 WebAssembly 字节码,在欣赏器中运行时,用户点击相应的按钮(通过绑定该方法到按钮的点击变乱),就能实现计数器的功能,而无需使用 JavaScript 来编写变乱处理逻辑。
(二)与传统前端开辟的区别

  1. @page "/"
  2. <h1>Hello, Blazor!</h1>
  3. <p>Count: @count</p>
  4. <button @onclick="IncrementCount">Increment</button>
  5. @code {
  6.     private int count = 0;
  7.     private void IncrementCount()
  8.     {
  9.         count++;
  10.     }
  11. }
复制代码
(三)优势亮点

三、开辟环境搭建

(一)必备工具




(二)创建项目


  1. dotnet new blazorwasm -o MyBlazorApp
复制代码

  1. cd MyBlazorApp
复制代码

(三)项目布局剖析

四、核心概念与技术

(一)Razor 语法

Razor 语法是 Blazor 中用于构建用户界面的关键技术,它巧妙地将 HTML 与 C# 代码融合在一起,为开辟者提供了一种轻便而高效的编程体验。在 Blazor 项目中,Razor 文件(.razor)是主要的代码载体,开辟者可以在同一个文件中同时编写前端的 UI 布局和后端的业务逻辑,这大大提高了代码的可读性和可维护性。
在 Razor 语法中,使用@符号来标识 C# 代码块或表达式。例如,要在 HTML 中显示一个 C# 变量的值,可以这样写:
  1. <p>当前计数: @count</p>
复制代码
这里的@count就是一个 Razor 表达式,它会在运行时被更换为count变量的实际值。
Razor 还支持各种指令,这些指令用于控制页面的行为和布局。其中,@page指令用于界说页面的路由,例如:
  1. @page "/home"
复制代码
表现该页面的路由为/home,当用户访问这个 URL 时,对应的组件就会被渲染。
此外,@using指令用于导入定名空间,使得在当前组件中可以使用该定名空间下的类型。好比:
  1. @using System.Net.Http
复制代码
通过这条指令,我们就可以在组件中使用System.Net.Http定名空间下的HttpClient等类型,方便进行 HTTP 请求操作。
(二)组件化开辟

组件化开辟是 Blazor 的核心特性之一,它允许开辟者将复杂的用户界面拆分成一个个独立的、可复用的组件,每个组件都有自己的逻辑和样式,通过组合这些组件,可以构建出复杂的界面。
创建一个 Blazor 组件非常简单,只须要创建一个以.razor为后缀的文件,在文件中界说组件的 UI 和逻辑。例如,下面是一个简单的计数器组件:
  1. @page "/counter"<h1>计数器</h1><p>当前计数: @count</p>
  2. <button @onclick="IncrementCount">增加计数</button>
  3. @code {    private int count = 0;    private void IncrementCount()    {        count++;    }}
复制代码
在这个组件中,@page "/counter"界说了组件的路由,用户访问/counter时会看到这个组件。
、和标签界说了组件的 UI,@code块中包罗了组件的 C# 逻辑代码,IncrementCount方法用于增加计数器的值。



组件之间可以通过参数传递数据,实现数据的共享和交互。在父组件中使用子组件时,可以通过属性绑定的方式将数据传递给子组件。例如,假设有一个ChildComponent子组件,它有一个Message参数:
  1. <ChildComponent Message="Hello, Blazor!" />
复制代码
在子组件ChildComponent.razor中,可以这样接收参数:
  1. @typeparam string Message
  2. <p>@Message</p>
复制代码
这里的@typeparam指令用于声明组件的参数类型。
每个组件都有自己的生命周期,包括初始化、渲染、更新和销毁等阶段。在这些阶段中,开辟者可以执行一些特定的操作。例如,OnInitializedAsync方法会在组件初始化后异步执行,通常用于加载数据等操作:
  1. protected override async Task OnInitializedAsync()
  2. {
  3.     // 从API获取数据
  4.     var data = await Http.GetFromJsonAsync<List<WeatherForecast>>("weatherforecast");
  5.     forecasts = data;
  6. }
复制代码
(三)数据绑定与变乱处理

数据绑定是 Blazor 实现数据与 UI 同步的重要机制,它分为单向数据绑定和双向数据绑定。
单向数据绑定是指数据从模型流向视图,当模型数据发生厘革时,UI 会主动更新。在 Blazor 中,使用@符号实现单向数据绑定。例如:
  1. <p>当前计数: @count</p>
复制代码
当count变量的值发生厘革时,
标签中的文本会主动更新。
双向数据绑定则允许数据在模型和视图之间双向流动,用户在 UI 上的操作会实时反映到模型中,模型的厘革也会立刻更新到 UI 上。Blazor 使用@bind指令实现双向数据绑定,主要用于表单元素等场景。例如:
  1. <input @bind="userName" />
  2. <p>用户名: @userName</p>
复制代码
在这个例子中,当用户在输入框中输入内容时,userName变量的值会实时更新;反之,当userName变量的值在代码中被修改时,输入框中的内容也会相应改变。
变乱处理是 Blazor 实现用户交互的关键,通过绑定变乱处理方法,当用户触发某个变乱(如点击按钮、输入内容等)时,相应的方法会被执行。例如,在前面的计数器组件中,按钮的点击变乱绑定了IncrementCount方法:
  1. <button @onclick="IncrementCount">增加计数</button>
复制代码
当用户点击按钮时,IncrementCount方法会被调用,实现计数器增加的功能。
(四)依赖注入

依赖注入是一种软件筹划模式,它允许将依赖对象(如服务、数据库连接等)通过外部提供的方式注入到组件中,而不是在组件内部直接创建,这样可以提高代码的可测试性、可维护性和可扩展性。
在 Blazor 中,使用.NET Core 内置的依赖注入容器来管理依赖关系。首先,须要在Program.cs文件中注册服务。例如,注册一个WeatherForecastService服务:
  1. builder.Services.AddScoped<WeatherForecastService>();
复制代码
这里使用AddScoped方法表现该服务在每个请求范围内是唯一的,即每次请求都会创建一个新的服务实例。
在组件中使用依赖注入时,可以通过@inject指令大概属性注入的方式获取服务实例。例如:
  1. @page "/fetchdata"
  2. @inject WeatherForecastService ForecastService
  3. <h1>天气预报</h1>
  4. @if (forecasts == null)
  5. {
  6.     <p><em>加载中...</em></p>
  7. }
  8. else
  9. {
  10.     <table class="table">
  11.         <thead>
  12.             <tr>
  13.                 <th>日期</th>
  14.                 <th>温度 (C)</th>
  15.                 <th>温度 (F)</th>
  16.                 <th>摘要</th>
  17.             </tr>
  18.         </thead>
  19.         <tbody>
  20.             @foreach (var forecast in forecasts)
  21.             {
  22.                 <tr>
  23.                     <td>@forecast.Date.ToShortDateString()</td>
  24.                     <td>@forecast.TemperatureC</td>
  25.                     <td>@forecast.TemperatureF</td>
  26.                     <td>@forecast.Summary</td>
  27.                 </tr>
  28.             }
  29.         </tbody>
  30.     </table>
  31. }
  32. @code {
  33.     private WeatherForecast[] forecasts;
  34.     protected override async Task OnInitializedAsync()
  35.     {
  36.         forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
  37.     }
  38. }
复制代码
在这个组件中,通过@inject WeatherForecastService ForecastService注入了WeatherForecastService服务,然后在OnInitializedAsync方法中使用该服务获取天气预报数据并显示在页面上。
(五)路由与导航

路由是 Blazor 应用中实现页面导航和页面切换的关键机制,它允许根据差别的 URL 地址加载相应的组件。在 Blazor 中,使用@page指令来界说组件的路由。例如:
  1. @page "/home"
  2. <h1>首页</h1>
复制代码
表现该组件的路由为/home,当用户访问/home时,这个组件会被渲染。
实现页面导航可以使用NavLink组件,它会根据当前的 URL 主动添加或移除active类,以指示当前激活的链接。例如:
  1. <NavLink href="/home">首页</NavLink>
  2. <NavLink href="/counter">计数器</NavLink>
  3. <NavLink href="/fetchdata">获取数据</NavLink>
复制代码
点击这些链接时,会根据href属性的值进行页面导航,加载相应的组件。
在处理路由参数时,Blazor 支持在路由中界说参数。例如,界说一个带有参数的路由:
  1. @page "/product/{productId}"
复制代码
这里的{productId}就是一个路由参数,在组件中可以通过[Parameter]特性来接收这个参数:
  1. @page "/product/{productId}"
  2. @using System.Net.Http
  3. @inject HttpClient Http<h1>产品详情</h1>@if (product == null){    <p><em>加载中...</em></p>}else{    <p>产品ID: @product.Id</p>    <p>产品名称: @product.Name</p>    <p>产品价格: @product.Price</p>}@code {    [Parameter]    public int productId { get; set; }    private Product product;    protected override async Task OnInitializedAsync()    {        product = await Http.GetFromJsonAsync<Product>($"products/{productId}");    }}
复制代码
在这个例子中,productId参数会在组件初始化时被赋值,然后通过 HTTP 请求获取对应的产品数据并显示在页面上。
五、实战案例:构建一个简单的 Blazor WebAssembly 应用

(一)需求分析

本次我们要构建一个简单的使命管理应用,它主要包罗以下功能:
(二)筹划与实现

  1. dotnet new blazorwasm -o TaskManagerApp
复制代码
然后进入项目目录:
  1. cd TaskManagerApp
复制代码
  1. public class Task
  2. {
  3.     public int Id { get; set; }
  4.     public string Name { get; set; }
  5.     public DateTime CreatedTime { get; set; }
  6.     public DateTime DueTime { get; set; }
  7.     public bool IsCompleted { get; set; }
  8. }
复制代码
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. public class TaskService
  6. {
  7.     private List<Task> tasks = new List<Task>();
  8.     public TaskService()
  9.     {
  10.         // 初始化一些示例数据
  11.         tasks.Add(new Task
  12.         {
  13.             Id = 1,
  14.             Name = "学习Blazor WebAssembly",
  15.             CreatedTime = DateTime.Now,
  16.             DueTime = DateTime.Now.AddDays(3),
  17.             IsCompleted = false
  18.         });
  19.         tasks.Add(new Task
  20.         {
  21.             Id = 2,
  22.             Name = "完成项目文档",
  23.             CreatedTime = DateTime.Now,
  24.             DueTime = DateTime.Now.AddDays(1),
  25.             IsCompleted = false
  26.         });
  27.     }
  28.     public async Task<List<Task>> GetTasksAsync()
  29.     {
  30.         return tasks;
  31.     }
  32.     public async Task AddTaskAsync(Task newTask)
  33.     {
  34.         newTask.Id = tasks.Count + 1;
  35.         newTask.CreatedTime = DateTime.Now;
  36.         tasks.Add(newTask);
  37.     }
  38.     public async Task DeleteTaskAsync(int id)
  39.     {
  40.         var taskToDelete = tasks.FirstOrDefault(t => t.Id == id);
  41.         if (taskToDelete!= null)
  42.         {
  43.             tasks.Remove(taskToDelete);
  44.         }
  45.     }
  46. }
复制代码

  1. @page "/tasks"
  2. @using TaskManagerApp
  3. @inject TaskService TaskService
  4. <h1>任务列表</h1>
  5. <ul>
  6.     @foreach (var task in tasks)
  7.     {
  8.         <li>
  9.             @task.Name - 创建时间: @task.CreatedTime - 截止时间: @task.DueTime - 状态: @(task.IsCompleted? "已完成" : "未完成")
  10.             <button @onclick="@(() => DeleteTask(task.Id))">删除</button>
  11.         </li>
  12.     }
  13. </ul>
  14. <NavLink href="/addtask">添加任务</NavLink>
  15. @code {
  16.     private List<Task> tasks;
  17.     protected override async Task OnInitializedAsync()
  18.     {
  19.         tasks = await TaskService.GetTasksAsync();
  20.     }
  21.     private async Task DeleteTask(int id)
  22.     {
  23.         await TaskService.DeleteTaskAsync(id);
  24.         tasks = await TaskService.GetTasksAsync();
  25.         StateHasChanged();
  26.     }
  27. }
复制代码

  1. @page "/addtask"
  2. @using TaskManagerApp
  3. @inject TaskService TaskService
  4. @inject NavigationManager NavigationManager
  5. <h1>添加任务</h1>
  6. <EditForm Model="newTask" OnValidSubmit="HandleValidSubmit">
  7.     <DataAnnotationsValidator />
  8.     <ValidationSummary />
  9.     <div class="mb-3">
  10.         <label for="taskName" class="form-label">任务名称</label>
  11.         <InputText id="taskName" @bind-Value="newTask.Name" class="form-control" />
  12.     </div>
  13.     <div class="mb-3">
  14.         <label for="dueTime" class="form-label">截止时间</label>
  15.         <InputDate id="dueTime" @bind-Value="newTask.DueTime" class="form-control" />
  16.     </div>
  17.     <button type="submit" class="btn btn-primary">提交</button>
  18. </EditForm>
  19. @code {
  20.     private Task newTask = new Task();
  21.     private async Task HandleValidSubmit()
  22.     {
  23.         await TaskService.AddTaskAsync(newTask);
  24.         NavigationManager.NavigateTo("/tasks");
  25.     }
  26. }
复制代码
  1. <Router AppAssembly="@typeof(Program).Assembly">
  2.     <Found Context="routeData">
  3.         <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
  4.     </Found>
  5.     <NotFound>
  6.         <LayoutView Layout="@typeof(MainLayout)">
  7.             <p>抱歉,未找到该页面。</p>
  8.         </LayoutView>
  9.     </NotFound>
  10. </Router>
复制代码
  1. builder.Services.AddScoped<TaskService>();
复制代码
  1. <NavLink href="/tasks" Match="NavLinkMatch.All">任务列表</NavLink>
  2. <NavLink href="/addtask" Match="NavLinkMatch.All">添加任务</NavLink>
复制代码
(三)效果展示与题目解决


六、性能优化与安全考量

(一)性能优化策略

  1. <LazyComponentLoader Name="MyComponent" />
复制代码
这里的MyComponent会在须要显示时才被加载,而不是在应用启动时就全部加载。
  1. private readonly IMemoryCache _memoryCache;
  2. public TaskService(IMemoryCache memoryCache)
  3. {
  4.     _memoryCache = memoryCache;
  5. }
  6. public async Task<List<Task>> GetTasksAsync()
  7. {
  8.     if (!_memoryCache.TryGetValue("Tasks", out List<Task> tasks))
  9.     {
  10.         // 从数据源获取任务数据
  11.         tasks = await GetTasksFromDataSource();
  12.         // 将任务数据缓存起来
  13.         _memoryCache.Set("Tasks", tasks, TimeSpan.FromMinutes(10));
  14.     }
  15.     return tasks;
  16. }
复制代码
(二)安全题目与防护

  1. <input type="hidden" name="__RequestVerificationToken" value="@(await Html.AntiForgeryTokenAsync())" />
复制代码
服务器在接收到请求时,会验证令牌的有效性,如果令牌无效,则拒绝请求。
\3. 输入验证:对用户输入的数据进行严酷的验证是防止安全漏洞的重要措施。在 Blazor 中,可以使用数据注解(如[Required]、[StringLength]等)来进行输入验证。在Task模型中,可以添加数据注解:
  1. public class Task
  2. {
  3.     public int Id { get; set; }
  4.     [Required]
  5.     [StringLength(100)]
  6.     public string Name { get; set; }
  7.     public DateTime CreatedTime { get; set; }
  8.     public DateTime DueTime { get; set; }
  9.     public bool IsCompleted { get; set; }
  10. }
复制代码
在AddTask.razor组件中,使用EditForm和DataAnnotationsValidator组件来进行表单验证,确保用户输入的数据符合要求。
七、与后端服务集成

(一)调用 API

在 Blazor WebAssembly 应用中,与后端服务进行集成是实现丰富功能的关键步调。调用后端的 RESTful API 是获取和提交数据的常见方式,而HttpClient则是 Blazor 中用于进行 HTTP 请求的重要工具。
首先,在Program.cs文件中注册HttpClient服务:
  1. builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
复制代码
上述代码将HttpClient注册为作用域服务,并设置了请求的基地址。在实际应用中,你可以根据后端 API 的地址进行相应的修改。
接下来,在组件中注入HttpClient并发起请求。例如,在一个获取产品列表的组件中:
  1. @page "/products"
  2. @inject HttpClient Http
  3. <h1>产品列表</h1>
  4. @if (products == null)
  5. {
  6.     <p><em>加载中...</em></p>
  7. }
  8. else
  9. {
  10.     <ul>
  11.         @foreach (var product in products)
  12.         {
  13.             <li>@product.Name - @product.Price</li>
  14.         }
  15.     </ul>
  16. }
  17. @code {
  18.     private Product[] products;
  19.     protected override async Task OnInitializedAsync()
  20.     {
  21.         products = await Http.GetFromJsonAsync<Product[]>("api/products");
  22.     }
  23. }
复制代码
在这个例子中,Http.GetFromJsonAsync方法用于发送 GET 请求到api/products端点,并将相应数据反序列化为Product数组。该方法会主动处理 JSON 数据的解析,非常方便。
如果须要发送 POST 请求来创建新的产品,可以这样实现:
  1. private async Task CreateProduct(Product newProduct)
  2. {
  3.     var response = await Http.PostAsJsonAsync("api/products", newProduct);
  4.     if (response.IsSuccessStatusCode)
  5.     {
  6.         // 处理成功响应
  7.         var createdProduct = await response.Content.ReadFromJsonAsync<Product>();
  8.         // 更新产品列表
  9.         products = products.Append(createdProduct).ToArray();
  10.         StateHasChanged();
  11.     }
  12.     else
  13.     {
  14.         // 处理错误响应
  15.         Console.WriteLine($"请求失败,状态码: {response.StatusCode}");
  16.     }
  17. }
复制代码
在上述代码中,Http.PostAsJsonAsync方法用于发送 POST 请求,将新的产品数据以 JSON 格式发送到api/products端点。根据相应的状态码来判断请求是否成功,如果成功则读取相应数据并更新产品列表。
(二)数据交互格式

在前后端交互中,数据交互格式的选择至关重要。JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,由于其轻便、易读、易于解析和生成的特点,在 Web 开辟中被广泛应用,Blazor 也不例外。
JSON 数据以键值对的形式构造,例如:
  1. {
  2.     "name": "Blazor Book",
  3.     "price": 49.99,
  4.     "isAvailable": true
  5. }
复制代码
在 Blazor 中,处理 JSON 数据非常方便。前面提到的HttpClient的GetFromJsonAsync和PostAsJsonAsync等方法,就是专门用于处理 JSON 数据的。这些方法基于System.Text.Json定名空间下的功能,可以或许主动将 JSON 数据与 C# 对象进行相互转换。
除了使用HttpClient的便捷方法外,还可以手动使用System.Text.Json定名空间下的类来处理 JSON 数据。例如,将一个 C# 对象序列化为 JSON 字符串:
  1. using System.Text.Json;
  2. var product = new Product { Name = "New Product", Price = 29.99, IsAvailable = true };
  3. string json = JsonSerializer.Serialize(product);
复制代码
上述代码使用JsonSerializer.Serialize方法将Product对象转换为 JSON 字符串。
反之,将 JSON 字符串反序列化为 C# 对象:
  1. string json = "{"name":"New Product","price":29.99,"isAvailable":true}";
  2. Product product = JsonSerializer.Deserialize<Product>(json);
复制代码
这里使用JsonSerializer.Deserialize方法将 JSON 字符串转换为Product对象。在反序列化时,要确保 C# 对象的属性名称与 JSON 中的键名同等,大概通过JsonPropertyName特性进行映射。
(三)状态管理与同步

在前后端交互过程中,状态管理和数据同步是确保应用步伐正常运行和用户体验的重要环节。由于 Blazor 应用是基于组件的,差别组件之间可能须要共享和同步数据,同时在与后端交互时,也须要包管数据的同等性。
一种简单的状态管理方式是通过依赖注入实现。例如,创建一个状态管理服务ProductStateService:
  1. public class ProductStateService
  2. {
  3.     private Product[] products;
  4.     public Product[] Products
  5.     {
  6.         get => products;
  7.         set
  8.         {
  9.             products = value;
  10.             StateChanged?.Invoke();
  11.         }
  12.     }
  13.     public event Action StateChanged;
  14. }
复制代码
在这个服务中,Products属性用于存储产品数据,当该属性的值发生厘革时,会触发StateChanged变乱。
在Program.cs文件中注册该服务:
  1. builder.Services.AddSingleton<ProductStateService>();
复制代码
在组件中注入并使用该服务:
  1. @page "/products"
  2. @inject ProductStateService ProductState
  3. @inject HttpClient Http
  4. <h1>产品列表</h1>
  5. @if (ProductState.Products == null)
  6. {
  7.     <p><em>加载中...</em></p>
  8. }
  9. else
  10. {
  11.     <ul>
  12.         @foreach (var product in ProductState.Products)
  13.         {
  14.             <li>@product.Name - @product.Price</li>
  15.         }
  16.     </ul>
  17. }
  18. @code {
  19.     protected override async Task OnInitializedAsync()
  20.     {
  21.         ProductState.Products = await Http.GetFromJsonAsync<Product[]>("api/products");
  22.     }
  23. }
复制代码
当后端数据发生厘革时,可以通过调用ProductStateService的方法来更新状态,并触发StateChanged变乱,从而通知相关组件进行重新渲染,实现数据同步。
对于更复杂的状态管理场景,可以使用第三方状态管理库,如 Fluxor。Fluxor 基于 Redux 的架构头脑,提供了一种单向数据流的状态管理模式,使得状态的厘革更加可猜测和易于维护。使用 Fluxor 时,须要界说状态、动作和归约函数,通过分发动作来更新状态。例如:
  1. var currentAssembly = typeof(Program).Assembly;
  2. builder.Services.AddFluxor(options => options.ScanAssemblies(currentAssembly));
复制代码
  1. using Fluxor;
  2. namespace BlazorApp.Store;
  3. [FeatureState]
  4. public class ProductState
  5. {
  6.     public Product[] Products { get; private set; }
  7.     private ProductState() { }
  8.     public ProductState(Product[] products)
  9.     {
  10.         Products = products;
  11.     }
  12. }
复制代码
  1. namespace BlazorApp.Store;
  2. public class LoadProductsAction { }
复制代码
  1. using Fluxor;
  2. namespace BlazorApp.Store;
  3. public static class ProductReducers
  4. {
  5.     [ReducerMethod]
  6.     public static ProductState ReduceLoadProductsAction(ProductState state, LoadProductsAction action, HttpClient http)
  7.     {
  8.         var products = http.GetFromJsonAsync<Product[]>("api/products").Result;
  9.         return new ProductState(products);
  10.     }
  11. }
复制代码
  1. @page "/products"
  2. @inject IDispatcher Dispatcher
  3. @inject IState<ProductState> ProductState
  4. <h1>产品列表</h1>
  5. @if (ProductState.Value.Products == null)
  6. {
  7.     <p><em>加载中...</em></p>
  8. }
  9. else
  10. {
  11.     <ul>
  12.         @foreach (var product in ProductState.Value.Products)
  13.         {
  14.             <li>@product.Name - @product.Price</li>
  15.         }
  16.     </ul>
  17. }
  18. @code {
  19.     protected override void OnInitialized()
  20.     {
  21.         Dispatcher.Dispatch(new LoadProductsAction());
  22.     }
  23. }
复制代码
通过这种方式,Fluxor 帮助我们更好地管理复杂的状态,确保在前后端交互中数据的同等性和可维护性。
八、总结与展望

Blazor WebAssembly 作为一种创新的 Web 开辟技术,为开辟者带来了诸多便利与优势。它打破了传统前端开辟对 JavaScript 的单一依赖,允许开辟者使用 C# 这一强大的编程语言进行前端开辟。通过将 C# 代码编译为 WebAssembly 字节码在欣赏器中运行,Blazor WebAssembly 实现了高性能的前端应用,为用户提供了流畅的交互体验。
在开辟流程上,我们首先须要搭建好开辟环境,包括安装必备的工具如 Visual Studio 或 Visual Studio Code,以及最新版的.NET SDK。创建项目后,深入了解项目布局,如 wwwroot 文件夹用于存放静态文件,Pages 文件夹存放 Razor 页面组件,Shared 文件夹用于共享组件和布局文件等,这有助于我们更好地构造和管理代码。
核心概念与技术方面,Razor 语法让 HTML 与 C# 代码完美融合,实现了轻便高效的 UI 开辟;组件化开辟模式将复杂的界面拆分成可复用的组件,提高了代码的可维护性和可扩展性;数据绑定与变乱处理机制实现了数据与 UI 的同步以及用户与应用的交互;依赖注入则提高了代码的可测试性和可维护性;路由与导航功能实现了页面之间的切换和参数传递。
通过构建简单的使命管理应用这一实战案例,我们亲身体验了 Blazor WebAssembly 从需求分析、筹划实现到效果展示与题目解决的全过程,进一步加深了对其开辟流程和技术应用的理解。在性能优化与安全考量上,我们探讨了代码分割与懒加载、缓存机制等性能优化策略,以及 XSS 攻击防护、CSRF 攻击防护和输入验证等安全措施。
在与后端服务集成时,使用 HttpClient 调用 API 实现了前后端的数据交互,JSON 作为主要的数据交互格式,轻便高效。同时,通过依赖注入或第三方库如 Fluxor 进行状态管理与同步,确保了数据的同等性和应用的稳定性。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4