ASP.NET Core 3.x 学习笔记(7)——Blazor


本系列学习笔记均来源于B站UP主”软件工艺师“的学习视频,学习连接如下:

https://www.bilibili.com/video/BV1c441167KQ

ASP.NET Core 3.x 学习笔记(7)——Blazor

编程模式对比

MVC

在这里插入图片描述

SPA(Single Page Application):这里的 Static Html、Js、Css 不一定总是来源于 Server API,也可能是服务器其它地方。这里主要体线的区别是 Browser 的 Javascript。

在这里插入图片描述

SPA(Blazor):Blazor 的 Browser 端使用 C#。

在这里插入图片描述

Blazor

  • Blazor 是基于 Component(组件)的编程模式,即 Blazor 是写组件的。
  • Blazor 的宿主模型分为:
    • 客户端
    • 服务器端

客户端宿主模型

如下图,mono 把 Server 端 C# 语言编写生成的 DLL,解析成了 Browser 中 浏览器可以理解的 WebAssembly,然后 Browser 就可以通过 Javascript 进行互操作。

在这里插入图片描述

优点:

  • 没有 .NET 服务器端依赖项。应用下载到客户端之后完全正常运行。
  • 完全利用客户端资源和功能。
  • 工作从服务器卸载到客户端,即工作量从服务器转移到客户端进行。
  • 不需要 ASP.NET Core web 服务器来托管应用程序,可能使用无服务器部署方案,例如,可通过 CDN 提供应用。

缺点:

  • 应用程序限制为浏览器的功能
  • 需要支持的客户端硬件和软件,例如 WebAssembly 支持
  • 下载的文件大小较大,应用需要较长时间才能加载
  • .NET 运行时和工具支持不太成熟。
Mono
  • mono 是一个开源的 .NET Framework
  • 它可以解释 IL(中间语言:intermediary language)
  • C# 代码的 IL 是包含在 .NET 的 Assembly(程序集:DLL)里面;(然后发送给浏览器)
  • 浏览器之所以可以执行 mono,是因为它接收到的 mono 版本是使用一种类似汇编(Assembly)的低级语言编写的。而浏览器可以理解这种语言,它就是 WebAssembly。
  • 然后,mono 就会把 Assembly 里面的代码(包含着 Components)解析成为 WebAssembly。这样就可以在浏览器里面运行了。

服务器端宿主模型

服务器端(Server)对 Components 完成渲染,将渲染好的 Components 传递到浏览器(Browser)

浏览器(Browser)将前端互操作传递到服务器端(Server),服务器端对其重新进行渲染并把更新后的 UI 发送到浏览器(Browser)。

两者之间通过 SignalR 传输。

在这里插入图片描述

优点:

  • 下载大小明显小于客户的应用,且应用加载速度更快。
  • 应用充分利用服务器功能,包括使用任何与 .NET Core 兼容的 API。
  • 服务器上的 .NET Core 用于运行应用程序,因此现有的 .NET Core 工具(如调试),可按预期方式工作。
  • 支持廋客户端,例如服务器端应用程序适用于不支持 web assembly 的浏览器和资源限制的设备。因为所有的工作都是在服务器中处理的。
  • 应用程序的 .NET/C# 代码库(包括应用程序的组件代码)不会提供给客户端。

缺点:

  • 通常存在较高的延迟,每个用户交互都涉及网络跃点
  • 无脱机支持。如果客户端连接失败,应用将停止工作。
  • 对于包含多个用户的应用而言,可伸缩性非常困难,服务器必须管理多个客户端连接并处理客户端状态
  • 为用户提供服务需要 ASP.NET Core 服务器。不可能的无服务器部署方案。例如,可通过 CDN 为应用提供服务。

从 Empty 模板开始建立一个 Blazor 项目

  1. Models 和 Services 和之前的 MVC 项目没有区别,可以直接拿过来使用;静态文件也可以直接拿过来用。

  2. 在 Startup.cs 中添加 Blazor 项目需要的配置和需要使用到的中间件

    using ASPNETCore_Learnting_Blazor.Services;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace ASPNETCore_Learnting_Blazor
    {
        public class Startup
        {
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddRazorPages();  //Blazor 需要使用到 Razor 页面/组件
                services.AddServerSideBlazor();  //添加服务器端的 Blazor
    
                //下面是注入项目中需要使用的实体类
                services.AddSingleton<IClock, ChinaClock>(); 
                services.AddSingleton<IDepartmentService, DepartmentService>();
                services.AddSingleton<IEmployeeService, EmployeeService>();
            }
    
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseStaticFiles();
                app.UseRouting
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapBlazorHub();  //类似于一个 特制版的 SingalR Hub
                    endpoints.MapFallbackToPage("/_Host");  //设置默认启动页面,会找到当前项目路径下的“/Pages/_Host.cshtml”文件
                });
            }
        }
    }
    
  3. 建立默认页面”/Pages/_Host.cshtml“。这里通过路由设置具体的页面(Razor Components),这里的 App 进行路由设置。

    @page "/"
    @namespace ASPNETCore_Learnting_Blazor.Pages
    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Blazor</title>
        <base href="~/" />
        <link rel="stylesheet" href="~/css/bootstrap.css" />
        <link rel="stylesheet" href="~/css/site.css" />
    </head>
    <body>
        <app>
            @* 这里通过路由设置具体的页面(Razor Components),这里的 App 进行路由处理 *@
            @(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
        </app>
    
        <script src="_framework/blazor.server.js"></script>
    </body>
    </html>
    
  4. 建立处理路由的文件,”/App.razor“。这里的 MainLayout 指的是模板页面,即布局页面。

    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry.</p>
            </LayoutView>
        </NotFound>
    </Router>
    
  5. 建立处理布局页面,”/Shared/MainLayout.razor“。每个具体页面的内容在下面页面代码的 @Body 处。

    @inherits Microsoft.AspNetCore.Components.LayoutComponentBase
    
    <div class="container">
        <div class="row">
            <div class="col-md-2">
                <img asp-append-version="true" alt="Logos" src="~/images/log.jpg" style="max-width:100%" />
            </div>
            <div class="col-md-10">
                <span class="h3">Blazor</span>
            </div>
        </div>
        <div class=" row">
            <div class="col-md-10">
                @Body
            </div>
        </div>
    </div>
    
  6. 添加公用的引用文件 ”/_importants.razor“。如下添加的引用不是一次性加入的,是在后续编写代码的过程中一步步补充的 。

    @using System.Net.Http
    @using Microsoft.AspNetCore.Components.Forms
    @using Microsoft.AspNetCore.Components.Routing
    @using Microsoft.AspNetCore.Components.Web
    @using Microsoft.JSInterop
    @using ASPNETCore_Learnting_Blazor.Models
    @using ASPNETCore_Learnting_Blazor.Services
    @using ASPNETCore_Learnting_Blazor.Shared
    @using ASPNETCore_Learnting_Blazor.Components
    
  7. 接下来实现具体的页面。

    • 首先实现 "/Pages/Index.razor"页面。如下 @page 是设置页面路由;@inject 是注入相关实例。

      如下代码中,在 @Code 代码块中可以编写处理相关业务逻辑的 C# 代码。

      @page "/"
      @inject IDepartmentService departmentService
      
      @if (departments == null)
      {
          <p><em>加载中......</em></p>
      }
      else
      {
          <div class="row">
              <div class="col-md-10 offset-md-2">
                  <table class="table">
                      <tr>
                          <th>Name</th>
                          <th>Location</th>
                          <th>Employee</th>
                          <th>操作</th>
                      </tr>
                      @foreach (var item in departments)
                      {
                          <DepartmentItem department="@item"></DepartmentItem>
                      }
                  </table>
              </div>
          </div>
          <div class="row">
              <div class="col-md-4 offset-md-2">
                  <a href="/add-department">Add</a>
              </div>
          </div>
      }
      
      @code {
          IEnumerable<Department> departments;
      
          protected override async Task OnInitializedAsync()
          {
              departments = await departmentService.GetAll();
          }
      }
      
    • 如上代码,DepartmentItem 是表征每一行 Department 数据而建立的 Razor Compontents,在“/Components”文件夹下。

      其中,@Department 是传递进来的实例,需要添加 [Parameter],在代码中即可获取相关参数。

      <tr>
          <td>@Department.Name</td>
          <td>@Department.Location</td>
          @if (Department.EmployeeCount > 18)
          {
              <td><strong>@Department.EmployeeCount</strong></td>
          }
          else
          {
              <td>@Department.EmployeeCount</td>
          }
      
          <td>
              <a href="/employee/@Department.Id">Employees</a>
          </td>
      </tr>
      
      @code{
          [Parameter]
          public Department Department { get; set; }
      }
      
    • 添加增加部门的 AddDepartment.razor 页面,关联到上文中的 “/add-department” 路由。

      如下表单代码中:

      1. EditForm InputText 等是内置的 Components
      2. OnValidSubmit="@HandleValidSubmit" 绑定表单提交时的业务逻辑处理函数
      3. DataAnnotationsValidator、ValidationSummary 等进行表单验证
      4. NavigationManager 进行路径跳转
      5. @bind-Value 进行参数值的双向绑定
      @page "/add-department"
      @inject IDepartmentService departmentService
      @inject NavigationManager navigationManager
      
      @* EditForm InputText 等是内置的 Components *@
      @*OnValidSubmit="@HandleValidSubmit" 表单提交*@
      <EditForm Model="@department" OnValidSubmit="@HandleValidSubmit">
          @* 表单验证 *@
          <DataAnnotationsValidator />
          <ValidationSummary />
          <div class="row form-group">
              <div class="col-md-2 offset-md-2">
                  <label for="name">Name</label>
              </div>
              <div class="col-md-2 offset-md-2">
                  @*@bind-Value 双向绑定*@
                  <InputText id="name" class="form-control" @bind-Value="department.Name" />
              </div>
          </div>
          <div class="row form-group">
              <div class="col-md-2 offset-md-2">
                  <label for="location">Location</label>
              </div>
              <div class="col-md-2 offset-md-2">
                  <InputText id="location" class="form-control" @bind-Value="department.Location" />
              </div>
          </div>
          <div class="row form-group">
              <div class="col-md-2 offset-md-2">
                  <label for="employeeCount">EmployeeCount</label>
              </div>
              <div class="col-md-2 offset-md-2">
                  <InputNumber id="employeeCount" class="form-control" @bind-Value="department.EmployeeCount" />
              </div>
          </div>
          <div class="row">
              <div class="col-md-2 offset-md-2">
                  <button type="submit" class="btn btn-primary">Add</button>
              </div>
          </div>
      </EditForm>
      
      @code
      {
          private Department department = new Department();
          private async Task HandleValidSubmit()
          {
              await departmentService.Add(department);
              navigationManager.NavigateTo("/");
          }
      }
      
    • 添加 /Pages/Employee.razor、/Pages/AddEmployee.razor、/Components/EmployeeItem.razor 等页面,代码逻辑于前文大同小异。

      若 razor 中 C#逻辑较为复杂,若C#与razor放在一起,则文件会较大,故单独放置C#逻辑代码

      @page "/employee/{DepartmentId}"
      @inject IEmployeeService employeeService
      
      
      @if (Employees == null)
      {
          <p><em>Loading......</em></p>
      }
      else
      {
          <div class="row">
              <div class="col-md-10 offset-md-2">
                  <table class="table">
                      <tr>
                          <th>First Name</th>
                          <th>Last Name</th>
                          <th>Gender</th>
                          <th>Is Fired</th>
                          <th>Action</th>
                      </tr>
                      @foreach (var employee in Employees)
                      {
                          <EmployeeItem employee="@employee"></EmployeeItem>
                      }
                  </table>
                  <a href="/add-employee/@DepartmentId">Add</a>
              </div>
          </div>
      }
      
      
      
      
      @code{
          [Parameter]
          public string DepartmentId { get; set; }
      
          public IEnumerable<ASPNETCore_Learnting_Blazor.Models.Employee> Employees;
      
      
          //[Inject]
          //public IEmployeeService EmployeeService { get; set; }
      
          protected override async Task OnInitializedAsync()
          {
              Console.WriteLine(DepartmentId);
              Employees = await employeeService.GetByDepartmentId(int.Parse(DepartmentId));
              Console.WriteLine(Employees.Count());
          }
      }
      

      AddEmployee.razor :

      @page "/add-employee/{departmentId}"
      @inject IEmployeeService employeeService
      @inject NavigationManager navigationManager
      
      <EditForm Model="@employee" OnValidSubmit="@HandleValidSubmit">
          <DataAnnotationsValidator />
          <ValidationSummary />
          <div class="row form-group">
              <div class="col-md-2 offset-md-2">
                  <label for="firstName">First Name</label>
              </div>
              <div class="col-md-2 offset-md-2">
                  <InputText id="firstName" class="form-control" @bind-Value="employee.FirstName" />
              </div>
          </div>
          <div class="row form-group">
              <div class="col-md-2 offset-md-2">
                  <label for="lastName">Last Name</label>
              </div>
              <div class="col-md-2 offset-md-2">
                  <InputText id="lasName" class="form-control" @bind-Value="employee.LastName" />
              </div>
          </div>
          <div class="row form-group">
              <div class="col-md-2 offset-md-2">
                  <label for="gender">Gender</label>
              </div>
              <div class="col-md-2 offset-md-2">
                  <select id="gender" class="form-control" @onchange="OnGenderSelected">
                      <option selected hidden disabled>Please select gender</option>
                      @foreach (var item in Enum.GetValues(typeof(Gender)).Cast<Gender>())
                      {
                          <option value="@item">@item.ToString()</option>
                      }
                  </select>
              </div>
          </div>
          <div class="row">
              <div class="col-md-2 offset-md-2">
                  <button type="submit" class="btn btn-primary">Add</button>
              </div>
          </div>
      
      
      </EditForm>
      
      @code
      {
          [Parameter]
          public string departmentId { get; set; }
      
          public ASPNETCore_Learnting_Blazor.Models.Employee employee = new ASPNETCore_Learnting_Blazor.Models.Employee();
      
          private void OnGenderSelected(ChangeEventArgs e)
          {
              var gender = Enum.Parse(typeof(Gender), (string)e.Value);
              employee.Gender = (Gender)gender;
      
          }
          public async Task HandleValidSubmit()
          {
              employee.DepartmentId = int.Parse(departmentId);
              await employeeService.Add(employee);
              navigationManager.NavigateTo($"/employee/{departmentId}");
          }
      }
      

      EmployeeItem.razor

      @inject IEmployeeService employeeService
      
      <tr>
          <td>@employee.FirstName</td>
          <td>@employee.LastName</td>
          <td>@employee.Gender</td>
          <td>@(employee.Fired ? "YES" : "")</td>
          <td>
              <a href="javascript:void(0)" @onclick="HandleFire">
                  Fire
              </a>
          </td>
      </tr>
      
      @code{
          [Parameter]
          public Employee employee { get; set; }
      
          private async void HandleFire()
          {
              await employeeService.Fire(employee.Id);
          }
      }
      
  8. 完成新建后的项目结构和效果图如下:

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值