Blazor WebAssembly极简学习(一)


Blazor的两种方式

1。基于WebAssembly

WebAssembly(简称Wasm)是一个虚拟指令集体系架构(virtual ISA),旨在提供一种高效、
安全和可移植的代码执行方式,特别是在Web平台上。

2。 运行在Server上


特点:

以上两种方式无需插件,基于Web标准。例如flash这种就需要装插件。

可以与Javascript进行交互。

可以利用.Net的优势,例如性能、安全性、现有的程序库等


Blazor的历史:

image.png


Blazor的两种宿主模型

。客户端

。服务器端


至其它的方式,如果桌面、移动平台等,在2020年微软承诺会提供。

但是截止勇哥写此文的时间是2024.6月,微软的承诺实现情况如下:

(1)Blazor Hybrid

利用 .NET MAUI 中的 BlazorWebView 控件,实现桌面开发的承诺。

微软的官方解释是:

.NET 多平台应用 UI (.NET MAUI) BlazorWebView 是一个控件,
可用于在 .NET MAUI 应用中托管 Blazor Web 应用。 
这些应用称为 Blazor 混合应用,使 Blazor Web 应用能够与平台功能和 UI 控件集成。 B
lazorWebView 控件可以添加到 .NET MAUI 应用的任何页面,
并指向 Blazor 应用的根目录。 Razor 组件在 .NET 进程中本机运行,
并将 Web UI 呈现到嵌入式 Web 视图控件。 
在 .NET MAUI 中,Blazor 混合应用可以在 .NET MAUI 支持的所有平台上运行。

原文地址:https://learn.microsoft.com/zh-cn/dotnet/maui/user-interface/controls/blazorwebview?view=net-maui-8.0&source=recommendations

勇哥的理解就相当于在winform程序里加了一个web浏览器控件,可以把winform+web界面混在一起。


(2)Blazor Native

在微软未来的计划中,Blazor Native 将支持让 Blazor 代码编译成目标平台原生的渲染方式

这其实就是一种自定义渲染器:

将 Blazor 的概念应用于原生应用,可能需要编写自定义的渲染器来将 Blazor 组件映射到原生 UI 控件。

这将是一个复杂的任务,需要深入了解 Blazor 和目标平台的 UI 系统。

这又是微软画的下一个大饼,我们就等着微软来折腾,至少目前是没有听到任何进展的消息。


(3)其它的开发Blazor桌面应用的方式

blazor的桌面开发微软只是提供了Blazor Hybrid方式,见上面的解释。它是利用了MAUI。

利用第三方工具也可以实现Blazor的桌面开发:

它就是 Electron.NET

Electron.NET 是一个允许你使用 .NET 和 Blazor 来构建跨平台桌面应用的框架。

它基于 Electron(一个使用 Web 技术构建跨平台桌面应用的开源库)但用 .NET 替代了 JavaScript。

用 Electron.NET,你可以创建类似于传统桌面应用的应用,

它们包含独立的窗口、本地菜单、文件对话框等,但使用 Blazor 编写前端代码。

安装和配置 Electron.NET 后,你可以使用 Blazor 组件来构建应用的 UI,

并使用 C# 来处理后端逻辑。

Electron的说明见:

https://www.electronjs.org/zh/docs/latest/

Electron.NET 见:

https://github.com/ElectronNET/electron.net-api-demos




客户端的Blazor

image.png

客户端的Blazor的优点:

。可以运行在所有现代浏览器上面,包括ipad手机平板等

。服务器端不需要.NET

。SPA的体验,即单页应用的体验


缺点:

。老一点浏览器不支持

。首次下载的应用比较大

。调试方便性差些


服务端的Blazor

image.png

服务器端的Blazor的优点:

。需要下载的东西很小

。可以使用所有服务器端的api

。完整的debug体验

。可以在不支持WebAssembly的浏览器中运行

缺点:

。不支持离线运行

。网络延迟影响较大

。可扩展性是个问题


服务器端的Blazor不是它的未来,未来最光明的还是Blazor WebAssembly。


下面来一段示例程序

需求:弄两个页面,一个是员工信息列表,一个是点击Detail后进入的详细页。

程序效果如下图示:

image.png

image.png


存储数据一般的方式:

1。后端,例如web api

2。 前端:例如Local Storage

这个例子中,勇哥把数据写死到代码里。


下面员工信息页的blazor组件代码。(创建时要选择blazor组件)

其中:

@page 是写的本页的路由地址

@inherits  写的是本html页绑定的处理类是  ListDatadisplayBase

    这种写法就相当于把页面和C#处理程序的类分离开来书写了。


@page "/ListDataDisplayView"
@inherits ListDataDisplayBase

<h1>Employee Overview</h1>
<table class="table table-hover table-striped">
   <thead class="thead-dark">
	   <tr>
		   <th>ID</th>
		   <th>部门</th>
		   <th>姓名</th>
		   <th>出生日期</th>
		   <th>性别</th>
		   <th>操作</th>
	   </tr>
   </thead>
   <tbody>
		@foreach (var m in Employess)
		{
			<tr>
				<td>@m.Id</td>
				<td>@m.DepartmentId</td>
				<td>@m.Name</td>
				<td>@m.BirthDate</td>
				<td>@m.Gender</td>
				<td>
					<a href="@($"UserDetail/{m.Id}")" class="btn btn-primary btn-sm">Detail</a>
				</td>
			</tr>
		}
	</tbody>
</table>

下面员工信息页的C#代码。(创建时要选择类)

注意几点:

1。 这个类必须继承ComponentBase

2。 写死的数据,放在这个类的生命周期方法中

using Microsoft.AspNetCore.Components;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace BlazorApp1.Pages
{
    public class ListDataDisplayBase: ComponentBase
    {
        public IEnumerable<EmployeeDto> Employess { get; set; }

        /// <summary>
        /// blazor的生命周期方法,异步
        /// </summary>
        /// <returns></returns>
        protected override Task OnInitializedAsync()
        {
            Employess = new List<EmployeeDto>()
            {
                new EmployeeDto
                {
                    Id = 1,DepartmentId = 1,No="A01",
                    Name="刘备",PictureUrl="",
                    BirthDate=new DateTime(1980, 1, 1),
                    Gender=Gender.Female
                },
                new EmployeeDto
                {
                      Id = 2,DepartmentId = 2,No="A02",
                    Name="张辽",PictureUrl="",
                    BirthDate=new DateTime(1972, 3, 12),
                    Gender=Gender.Female
                },
                new EmployeeDto
                {
                      Id = 3,DepartmentId = 3,No="A03",
                    Name="貂蝉",PictureUrl="",
                    BirthDate=new DateTime(1971, 5, 21),
                    Gender=Gender.Male
                },
                new EmployeeDto
                {
                    Id = 4,DepartmentId = 4,No="A04",
                    Name="吕布",PictureUrl="",
                    BirthDate=new DateTime(1982, 3, 22),
                    Gender=Gender.Female
                },
                new EmployeeDto
                {
                      Id = 5,DepartmentId = 5,No="A05",
                    Name="小桥",PictureUrl="",
                    BirthDate=new DateTime(1972, 1, 22),
                    Gender=Gender.Male
                },
                new EmployeeDto
                {
                      Id = 6,DepartmentId =6,No="A06",
                    Name="赵云",PictureUrl="",
                    BirthDate=new DateTime(1970, 5, 31),
                    Gender=Gender.Female
                }
            };
            return base.OnInitializedAsync();
        }
    }

    public class Department
    {
        public int Id { get; set; }
        [Display(Name="部门")]
        [Required(ErrorMessage ="{0}是必填项")]
        [MaxLength(50,ErrorMessage ="{0}的长度不能超过{1}")]
        public string Name { get; set; }
    }

    public class Employee
    {
        public int Id { get; set; }
        public int DepartmentID { get; set; }

        [Display(Name = "工号")]
        [Required(ErrorMessage = "{0}是必填项")]
        [StringLength(3,MinimumLength =3, ErrorMessage = "{0}的长度不能超过{1}")]
        public string No { get; set; }

        [Display(Name = "姓名")]
        [Required(ErrorMessage = "{0}是必填项")]
        [MaxLength(50, ErrorMessage = "{0}的长度不能超过{1}")]
        public string Name { get; set; }

        [Display(Name = "照片地址")]
        [MaxLength(500, ErrorMessage = "{0}的长度不能超过{1}")]
        public string PictureUrl { get; set; }

        [Display(Name = "日期")]
        public DateTime BirthDate { get; set; }

        [Display(Name = "性别")]
        public Gender Gender { get; set; }
    }

    public enum Gender
    {
        NotSpecified,
        Female,
        Male
    }

    public class EmployeeDto
    {
        public int Id { get; set; }
        public int DepartmentId { get; set; }

        [Display(Name = "工号")]
        public string No { get; set; }
        [Display(Name = "姓名")]
        public string Name { get; set; }
        [Display(Name = "照片地址")]
        public string PictureUrl { get; set; }
        [Display(Name = "日期")]
        public DateTime BirthDate { get; set; }
        [Display(Name = "性别")]
        public Gender Gender { get; set; }
    }

}

下面是详细页的组件代码

注意路由参数/UserDetail/{EmployeeId}的写法。

@page "/UserDetail/{EmployeeId}"
@inherits UserDetailBase

<h1>UserDetail for @Employee.Name</h1>
<idv>
	<div>@Employee.Id</div>
	<div>@Employee.DepartmentId</div>
	<div>@Employee.Name</div>
	<div>@Employee.BirthDate</div>
	<div>@Employee.Gender</div>
</idv>

下面是详细页的C#处理类代码:

几点说明:

1。 由于是写死的数据,所以这里还得重复写一次这些数据

2。 属性EmployeeId由是会做为组件页的路由参数,所以它得加上特性【Parameter】

using Microsoft.AspNetCore.Components;

namespace BlazorApp1.Pages
{
    public class UserDetailBase: ComponentBase
    {
        [Parameter]
        public string EmployeeId { get; set; }

        public EmployeeDto Employee { get; set; } = new EmployeeDto();

		public IEnumerable<EmployeeDto> Employees { get; set; }

		protected override Task OnInitializedAsync()
		{
			Employees = new List<EmployeeDto>
			{
				   new EmployeeDto
				{
					Id = 1,DepartmentId = 1,No="A01",
					Name="刘备",PictureUrl="",
					BirthDate=new DateTime(1980, 1, 1),
					Gender=Gender.Female
				},
				new EmployeeDto
				{
					  Id = 2,DepartmentId = 2,No="A02",
					Name="张辽",PictureUrl="",
					BirthDate=new DateTime(1972, 3, 12),
					Gender=Gender.Female
				},
				new EmployeeDto
				{
					  Id = 3,DepartmentId = 3,No="A03",
					Name="貂蝉",PictureUrl="",
					BirthDate=new DateTime(1971, 5, 21),
					Gender=Gender.Male
				},
				new EmployeeDto
				{
					Id = 4,DepartmentId = 4,No="A04",
					Name="吕布",PictureUrl="",
					BirthDate=new DateTime(1982, 3, 22),
					Gender=Gender.Female
				},
				new EmployeeDto
				{
					  Id = 5,DepartmentId = 5,No="A05",
					Name="小桥",PictureUrl="",
					BirthDate=new DateTime(1972, 1, 22),
					Gender=Gender.Male
				},
				new EmployeeDto
				{
					  Id = 6,DepartmentId =6,No="A06",
					Name="赵云",PictureUrl="",
					BirthDate=new DateTime(1970, 5, 31),
					Gender=Gender.Female
				}
			};
			Employee = Employees.SingleOrDefault(s => s.Id == int.Parse(EmployeeId));
			return base.OnInitializedAsync();
		}
	}
}



本文演示代码下载:


链接:https://pan.baidu.com/s/1CqVZervQmS_msCGlAzFPoQ 

提取码:9296 

--来自百度网盘超级会员V6的分享





本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:
本帖最后由 勇哥,很想停止 于 2024-06-04 21:10:31 编辑

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2025年4月    »
123456
78910111213
14151617181920
21222324252627
282930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864