asp td tr 高度_ASP.NET Core Blazor 初探之 Blazor Server

上周初步对Blazor WebAssembly进行了初步的探索(ASP.NET Core Blazor 初探之 Blazor WebAssembly)。这次来看看Blazor Server该怎么玩。

Blazor Server

Blazor 技术又分两种:

  • Blazor WebAssembly

  • Blazor Server

Blazor WebAssembly上次已经介绍过了,这次主要来看看Blazor Server。Blazor Server 有点像WebAssembly的服务端渲染模式。页面在服务器端渲染完成之后,通过SignalR(websocket)技术传输到前端,再替换dom元素。其实不光是页面的渲染,大部分计算也是服务端完成的。Blazor Server模式可以让一些不支持WebAssembly的浏览器可以运行Blazor项目,可是问题也是显而易见的,基于SignalR的双向实时通信给网络提出了很高的要求,一旦用户量巨大,对服务端的水平扩容也带来很大的挑战,Blazor Server的用户状态都维护在服务端,这对服务端内存也造成很大的压力。
我们还是以完成一个简单的CRUD项目为目标来探究一下Blazor Server究竟是什么。因为前面Blazor Webassembly已经讲过了,相同的东西,比如数据绑定,属性绑定,事件绑定等内容就不多说了,请参见ASP.NET Core Blazor 初探之 Blazor WebAssembly。

新建Blazor Server项目

打开vs找到Blazor Server模板,看清楚了不要选成Blazor Webassembly模板。

dcaab1c1bf6ff4b15c0b3679db0b8bdf.png

看看生成的项目结构:

fb674575a16a3f0604e82ce4fe2f0661.png

可以看到Blazor Server的项目结构跟ASP.Net Core razor pages 项目是一模一样的。看看Startup是怎么配置的:

public class Startup

{

public Startup(IConfiguration configuration)

{

Configuration = configuration;

}

public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.

// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940

public void ConfigureServices(IServiceCollection services)

{

services.AddRazorPages();

services.AddServerSideBlazor();

services.AddSingleton<WeatherForecastService>();

}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

{

if (env.IsDevelopment())

{

app.UseDeveloperExceptionPage();

}

else

{

app.UseExceptionHandler("/Error");

// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.

app.UseHsts();

}

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseEndpoints(endpoints =>

{

endpoints.MapBlazorHub();

endpoints.MapFallbackToPage("/_Host");

});

}

}

主要有2个地方要注意:在ConfigureServices方法里注册了Blazor的相关service:

services.AddServerSideBlazor();

在Configure方法的终结点配置了Blazor相关的映射:

endpoints.MapBlazorHub();

上次Blazor Webassembly我们的数据服务是通过一个Webapi项目提供的,这次不用了。如果需要提供webapi服务,Blazor Server本身就可以承载,但是Blazor Server根本不需要提供webapi服务,因为他的数据交互都是通过websocket完成的。

实现数据访问

新建student类:

public class Student

{

public int Id { get; set; }

public string Name { get; set; }

public string Class { get; set; }

public int Age { get; set; }

public string Sex { get; set; }

}

上次我们实现了一个StudentRepository,我们直接搬过来:

public interface IStudentRepository

{

List<Student> List();

Student Get(int id);

bool Add(Student student);

bool Update(Student student);

bool Delete(int id);

}

}

public class StudentRepository : IStudentRepository

{

private static List<Student> Students = new List<Student> {

new Student{ Id=1, Name="小红", Age=10, Class="1班", Sex="女"},

new Student{ Id=2, Name="小明", Age=11, Class="2班", Sex="男"},

new Student{ Id=3, Name="小强", Age=12, Class="3班", Sex="男"}

};

public bool Add(Student student)

{

Students.Add(student);

return true;

}

public bool Delete(int id)

{

var stu = Students.FirstOrDefault(s => s.Id == id);

if (stu != null)

{

Students.Remove(stu);

}

return true;

}

public Student Get(int id)

{

return Students.FirstOrDefault(s => s.Id == id);

}

public List<Student> List()

{

return Students;

}

public bool Update(Student student)

{

var stu = Students.FirstOrDefault(s => s.Id == student.Id);

if (stu != null)

{

Students.Remove(stu);

}

Students.Add(student);

return true;

}

}

注册一下:

services.AddScoped<IStudentRepository, StudentRepository>();

实现学生列表

跟上次一样,先删除默认生成的一些内容,减少干扰,这里不多说了。在pages文件夹下新建student文件夹,新建List.razor文件:

@page "/student/list"

@using BlazorServerDemo.Model

@using BlazorServerDemo.Data

@inject IStudentRepository Repository

Listh1>

<p class="text-right">

<a class="btn btn-primary" href="/student/add">Adda>

p>

<table class="table">

Idth>

Nameth>

Ageth>

Sexth>

Classth>

th>

tr>

@if (_stutdents != null)

{

foreach (var item in _stutdents)

{

@item.Idtd>

@item.Nametd>

@item.Agetd>

@item.Sextd>

@item.Classtd>

<a class="btn btn-primary" href="/student/modify/@item.Id">修改a>

<a class="btn btn-danger" href="/student/delete/@item.Id">删除a>

td>

tr>

}

}

table>

@code {

private List<Student> _stutdents;

protected override void OnInitialized()

{

_stutdents = Repository.List();

}

}

这个页面是从上次的WebAssembly项目上复制过来的,只改了下OnInitialized方法。上次OnInitialized里需要通过Httpclient从后台获取数据,这次不需要注入HttpClient了,只要注入Repository就可以直接获取数据。
运行一下:

5989b7e031f6775d2aab606eeb80072e.png

F12看一下这个页面是如何工作的:

f447e952e0a2093b83570132ba98673e.png

ce201adb5e13dbecf0db74f5319ec666.png

首先/student/list是一次标准的Http GET请求。返回了页面的html。从返回的html代码上来看绑定的数据已经有值了,这可以清楚的证明Blazor Server技术使用的是服务端渲染技术。

17d0e1d5257fe2b538de532620bea996.png

c9c162d8e5f0de948e3f89729e6f5e43.png

_blazor?id=Fv2IGD6CfKpQFZ-fi-e1IQ连接是个websocket长连接,用来处理服务端跟客户端的数据交互。

实现Edit组件

Edit组件直接从Webassembly项目复制过来,不用做任何改动。

@using BlazorServerDemo.Model

<div class="form-group">

Idlabel>

<input @bind="Student.Id" class="form-control" />

div>

<div class="form-group">

Namelabel>

<input @bind="Student.Name" class="form-control" />

div>

<div class="form-group">

Agelabel>

<input @bind="Student.Age" class="form-control" />

div>

<div class="form-group">

Classlabel>

<input @bind="Student.Class" class="form-control" />

div>

<div class="form-group">

Sexlabel>

<input @bind="Student.Sex" class="form-control" />

div>

<button class="btn btn-primary" @onclick="TrySave">

保存

button>

<CancelBtn Name="取消">CancelBtn>

div>

@code{

[Parameter]

public Student Student { get; set; }

[Parameter]

public EventCallback<Student> OnSaveCallback { get; set; }

protected override Task OnInitializedAsync()

{

if (Student == null)

{

Student = new Student();

}

return Task.CompletedTask;

}

private void TrySave()

{

OnSaveCallback.InvokeAsync(Student);

}

}

实现新增页面

同样新增页面从上次的Webassembly项目复制过来,可以复用大量的代码,只需改改保存的代码。原来保存代码是通过HttpClient提交到后台来完成的,现在只需要注入Repository调用Add方法即可。

@page "/student/add"

@using BlazorServerDemo.Model

@using BlazorServerDemo.Data

@inject NavigationManager NavManager

@inject IStudentRepository Repository

Addh1>

<Edit Student="Student" OnSaveCallback="OnSave">Edit>

<div class="text-danger">

@_errmsg

div>

@code {

private Student Student { get; set; }

private string _errmsg;

protected override Task OnInitializedAsync()

{

Student = new Student()

{

Id = 1

};

return base.OnInitializedAsync();

}

private void OnSave(Student student)

{

Student = student;

var result = Repository.Add(student);

if (result)

{

NavManager.NavigateTo("/student/list");

}

else

{

_errmsg = "保存失败";

}

}

}

这里不再多讲绑定属性,绑定事件等内容,因为跟Webassembly模式是一样的,请参见上一篇。
运行一下 :

12a3f9003d032f194e1cd2f9af316cad.png

我们的页面出来了。继续F12看看页面到底是怎么渲染出来的:

ca708b5c15e38ec045256db4a272954e.png

这次很奇怪并没有发生任何Http请求,那么我们的Add页面是哪里来的呢,让我们继续看Websocket的消息:

8ece523ad6954481ba182c45790dd555.png

c97cf16a5884522d4a40dbd2913b6959.png

客户端通过websocket给服务端发了一个消息,里面携带了一个信息:OnLocation Changed "http://localhost:59470/student/add",服务端收到消息后把对应的页面html渲染出来通过Websocket传递到前端,然后前端进行dom的切换,展示新的页面。所以这里看不到任何传统的Http请求的过程。
点一下保存看看发生了什么:

6c9f7bc5443a2b0f54aef9bdbe44b4dd.png

ea611bfa9f948b5c5780b3149d65b62a.png

我们可以看到点击保存的时候客户端同样没有发送任何Http请求,而是通过websocket给后台发了一个消息,这个消息表示哪个按钮被点击了,后台会根据这个信息找到需要执行的方法,方法执行完后通知前端进行页面跳转。
但是这里有个问题,我们填写的数据呢?我们在文本框里填写的数据貌似没有传递到后台,这就不符合逻辑了啊。想了下有可能是文本框编辑的时候数据就提交回去了,让我们验证下:

6481b2d57465cb5b0fca3908984480f1.png

我们一边修改文本框的内容,一边监控websocket的消息,果然发现了,当我们修改完焦点离开文本框的时候,数据直接被传递到了服务器。厉害了我的软,以前vue,angularjs实现的是前端html跟js对象的绑定技术,而Blazor Server这样就实现了前后端的绑定技术,666啊。

实现编辑跟删除页面

这个不多说了使用上面的知识点轻松搞定。
编辑页面:

@page "/student/modify/{Id:int}"

@using BlazorServerDemo.Model

@using BlazorServerDemo.Data

@inject NavigationManager NavManager

@inject IStudentRepository Repository

Modifyh1>

<Edit Student="Student" OnSaveCallback="OnSave">Edit>

<div class="text-danger">

@_errmsg

div>

@code {

[Parameter]

public int Id { get; set; }

private Student Student { get; set; }

private string _errmsg;

protected override void OnInitialized()

{

Student = Repository.Get(Id);

}

private void OnSave(Student student)

{

Student = student;

var result = Repository.Update(student);

if (result)

{

NavManager.NavigateTo("/student/list");

}

else

{

_errmsg = "保存失败";

}

}

}

删除页面:

@page "/student/delete/{Id:int}"

@using BlazorServerDemo.Model

@using BlazorServerDemo.Data

@inject NavigationManager NavManager

@inject IStudentRepository Repository

Deleteh1>

确定删除(@Student.Id)@Student.Name ?

h3>

<button class="btn btn-danger" @onclick="OnDeleteAsync">

删除

button>

<CancelBtn Name="取消">CancelBtn>

@code {

[Parameter]

public int Id { get; set; }

private Student Student { get; set; }

protected override void OnInitialized()

{

Student = Repository.Get(Id);

}

private void OnDeleteAsync()

{

var result = Repository.Delete(Id);

if (result)

{

NavManager.NavigateTo("/student/list");

}

}

}

运行一下:

3ae81e02abfc2f9a154e018b0270b6ae.png

a6914193a5a43cbcbdedcc9112d47ae5.png

总结

Blazor Server总体开发体验上跟Blazor Webassembly模式保持了高度一直。虽然是两种不同的渲染模式:Webassembly是客户端渲染,Server模式是服务端渲染。但是微软通过使用websocket技术作为一层代理,巧妙隐藏了两者的差异,让两种模式开发保持了高度的一致性。Blazor Server除了第一次请求使用Http外,其他数据交互全部通过websocket技术在服务端完成,包括页面渲染、事件处理、数据绑定等,这样给Blazor Server项目的网络、内存、扩展等提出了很大的要求,在项目选型上还是要慎重考虑。

最后demo的源码:BlazorServerDemo

关注我的公众号一起玩转技术

8eaa7dac0de7f2f0a497acc7ed357546.png

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值