Blazor 簡(jiǎn)介
Blazor 是一個(gè)使用 .NET 生成的交互式客戶端 Web UI 的框架。和前端同學(xué)所熟知的 Vue、React、Angular 有巨大差異。
其最大的特色是使用 C# 代碼(理論上可以是 .NET 生態(tài)的任何語言)代替 JavaScript 來實(shí)現(xiàn)邏輯。
使用 C# 代替 JavaScript 來創(chuàng)建信息豐富的交互式 UI。
共享使用 .NET 編寫的服務(wù)器端和客戶端應(yīng)用邏輯。
將 UI 呈現(xiàn)為 HTML 和 CSS,以支持眾多瀏覽器,其中包括移動(dòng)瀏覽器。
與新式托管平臺(tái)(如 Docker)集成。
使用 .NET 和 Blazor 生成混合桌面和移動(dòng)應(yīng)用。
使用 .NET 進(jìn)行客戶端 Web 開發(fā)可提供以下優(yōu)勢(shì):
使用 C# 代替 JavaScript 來編寫代碼。
利用現(xiàn)有的 .NET 庫生態(tài)系統(tǒng)。
在服務(wù)器和客戶端之間共享應(yīng)用邏輯。
受益于 .NET 的性能、可靠性和安全性。
使用開發(fā)環(huán)境(例如 Visual Studio 或 Visual Studio Code)保持 Windows、Linux 或 macOS 上的工作效率。
以一組穩(wěn)定、功能豐富且易用的通用語言、框架和工具為基礎(chǔ)來進(jìn)行生成。
有兩種不同開發(fā)模式
Blazor WebAssembly, C# 代碼運(yùn)行在瀏覽器中。 Blazor Server,C# 代碼在服務(wù)器端執(zhí)行,使用 SignalR 同步到瀏覽器進(jìn)行更新。
Blazor 是 apt.net core 生態(tài)的組成部分,所涉及到的技術(shù)也大部分和 .net 相關(guān)。
視圖層,使用 Razor 3 技術(shù)進(jìn)行前端的編排渲染。Razor是一種標(biāo)記語法,是 asp.net core 的默認(rèn)視圖語法,最顯著的特點(diǎn)是強(qiáng)類型(C#、VB等)的代碼可以和 HTML 寫在一個(gè)文件中,當(dāng)然也可以分開。在 razor 文件中,@符號(hào)后面的都是強(qiáng)類型語言,可以是一行中的一部分,也可以是一整行,還可以是一個(gè)段落。在 asp.net core 中的大致做法是把 VB、C# 等強(qiáng)類型語言嵌入到網(wǎng)頁,當(dāng)網(wǎng)頁被請(qǐng)求的時(shí)候,在服務(wù)器端執(zhí)行嵌入的代碼,動(dòng)態(tài)生成頁面。
以 Blazor WebAssembly 開發(fā)方式運(yùn)行時(shí),依賴 WebAssembly 4 技術(shù),可以做成靜態(tài)頁面不依賴任何后端服務(wù)器。
以 Blazor Server 方式開發(fā)運(yùn)行時(shí),依賴 SignalR 5 技術(shù),并且需要后端服務(wù)器端配合。
Bootstrap 風(fēng)格的 Blazor UI 組件庫 - BootstrapBlazor
基于 Bootstrap 樣式庫精心打造,并且額外增加了 100 多種常用的組件,為您快速開發(fā)項(xiàng)目帶來非一般的感覺.
參考資料?https://developer.mozilla.org/zh-CN/docs/Web/API/Element/requestFullScreen
Element.requestFullscreen() 方法用于發(fā)出異步請(qǐng)求使元素進(jìn)入全屏模式。
調(diào)用此API并不能保證元素一定能夠進(jìn)入全屏模式。如果元素被允許進(jìn)入全屏幕模式,返回的Promise會(huì)resolve,并且該元素會(huì)收到一個(gè)fullscreenchange (en-US)事件,通知它已經(jīng)進(jìn)入全屏模式。如果全屏請(qǐng)求被拒絕,返回的promise會(huì)變成rejected并且該元素會(huì)收到一個(gè)fullscreenerror (en-US)事件。如果該元素已經(jīng)從原來的文檔中分離,那么該文檔將會(huì)收到這些事件。
早期的Fullscreen API實(shí)現(xiàn)總是會(huì)把這些事件發(fā)送給document,而不是調(diào)用的元素,所以你自己可能需要處理這樣的情況。參考 Browser compatibility in [Page not yet written] 來得知哪些瀏覽器做了這個(gè)改動(dòng)。
注意:這個(gè)方法只能在用戶交互或者設(shè)備方向改變的時(shí)候調(diào)用,否則將會(huì)失敗。
語法
var Promise = Element.requestFullscreen(options);
參數(shù)
options 可選
一個(gè)FullscreenOptions (en-US)對(duì)象提供切換到全屏模式的控制選項(xiàng)。目前,唯一的選項(xiàng)是navigationUI (en-US),這控制了是否在元素處于全屏模式時(shí)顯示導(dǎo)航條UI。默認(rèn)值是"auto",表明這將由瀏覽器來決定是否顯示導(dǎo)航條。
返回值
在完成切換全屏模式后,返回一個(gè)已經(jīng)用值 undefined resolved的Promise
異常
requestFullscreen() 通過拒絕返回的 Promise來生成錯(cuò)誤條件,而不是拋出一個(gè)傳統(tǒng)的異常。拒絕控制器接收以下的某一個(gè)值:
TypeError
在以下幾種情況下,會(huì)拋出TypeError: 文檔中包含的元素未完全激活,也就是說不是當(dāng)前活動(dòng)的元素。 元素不在文檔之內(nèi)。 因?yàn)楣δ懿呗韵拗婆渲没蚱渌L問控制,元素不被允許使用"fullscreen"功能。 元素和它的文檔是同一個(gè)節(jié)點(diǎn)。
? ? ? ? bb_Fullscreen: function (ele) {
? ? ? ? ? ? ele.requestFullscreen() ||
? ? ? ? ? ? ? ? ele.webkitRequestFullscreen ||
? ? ? ? ? ? ? ? ele.mozRequestFullScreen ||
? ? ? ? ? ? ? ? ele.msRequestFullscreen;
? ? ? ? },
? ? ? ? bb_ExitFullscreen: function () {
? ? ? ? ? ? if (document.exitFullscreen) {
? ? ? ? ? ? ? ? document.exitFullscreen();
? ? ? ? ? ? }
? ? ? ? ? ? else if (document.mozCancelFullScreen) {
? ? ? ? ? ? ? ? document.mozCancelFullScreen();
? ? ? ? ? ? }
? ? ? ? ? ? else if (document.webkitExitFullscreen) {
? ? ? ? ? ? ? ? document.webkitExitFullscreen();
? ? ? ? ? ? }
? ? ? ? ? ? else if (document.msExitFullscreen) {
? ? ? ? ? ? ? ? document.msExitFullscreen();
? ? ? ? ? ? }
? ? ? ? }
? ? });
以下為簡(jiǎn)化代碼,運(yùn)行測(cè)試一下功能是否達(dá)到需求.
<button @onclick="FullScreen">全屏</button>
<button @onclick="ExitFullScreen">退出全屏</button>
@code{
[Inject] IJSRuntime? JSRuntime{ get; set; }
? //進(jìn)入全屏
? private Task FullScreen() => await JSRuntime.InvokeVoidAsync("bb_Fullscreen");
? //退出全屏
? private Task ExitFullScreen() => await JSRuntime.InvokeVoidAsync("bb_ExitFullscreen");
}
3.優(yōu)化邏輯,添加單按鈕全屏切換邏輯,添加針對(duì)單獨(dú)元素的全屏邏輯
JS完整代碼
(function ($) {
? ? $.extend({
? ? ? ? bb_toggleFullscreen: function (el, id) {
? ? ? ? ? ? var ele = el;
? ? ? ? ? ? if (!ele || ele === '') {
? ? ? ? ? ? ? ? if (id) {
? ? ? ? ? ? ? ? ? ? ele = document.getElementById(id);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? else {
? ? ? ? ? ? ? ? ? ? ele = document.documentElement;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? ? ? if ($.bb_IsFullscreen()) {
? ? ? ? ? ? ? ? $.bb_ExitFullscreen();
? ? ? ? ? ? ? ? ele.classList.remove('fs-open');
? ? ? ? ? ? }
? ? ? ? ? ? else {
? ? ? ? ? ? ? ? $.bb_Fullscreen(ele);
? ? ? ? ? ? ? ? ele.classList.add('fs-open');
? ? ? ? ? ? }
? ? ? ? },
? ? ? ? bb_Fullscreen: function (ele) {
? ? ? ? ? ? ele.requestFullscreen() ||
? ? ? ? ? ? ? ? ele.webkitRequestFullscreen ||
? ? ? ? ? ? ? ? ele.mozRequestFullScreen ||
? ? ? ? ? ? ? ? ele.msRequestFullscreen;
? ? ? ? },
? ? ? ? bb_ExitFullscreen: function () {
? ? ? ? ? ? if (document.exitFullscreen) {
? ? ? ? ? ? ? ? document.exitFullscreen();
? ? ? ? ? ? }
? ? ? ? ? ? else if (document.mozCancelFullScreen) {
? ? ? ? ? ? ? ? document.mozCancelFullScreen();
? ? ? ? ? ? }
? ? ? ? ? ? else if (document.webkitExitFullscreen) {
? ? ? ? ? ? ? ? document.webkitExitFullscreen();
? ? ? ? ? ? }
? ? ? ? ? ? else if (document.msExitFullscreen) {
? ? ? ? ? ? ? ? document.msExitFullscreen();
? ? ? ? ? ? }
? ? ? ? },
? ? ? ? bb_IsFullscreen: function () {
? ? ? ? ? ? return document.fullscreen ||
? ? ? ? ? ? ? ? document.webkitIsFullScreen ||
? ? ? ? ? ? ? ? document.webkitFullScreen ||
? ? ? ? ? ? ? ? document.mozFullScreen ||
? ? ? ? ? ? ? ? document.msFullScreent;
? ? ? ? }
? ? });
})(jQuery);
測(cè)試功能
<button @onclick="ToggleFullScreen">全屏</button>
@code{
[Inject] IJSRuntime? JSRuntime{ get; set; }
? //全屏方法,已經(jīng)全屏?xí)r再次調(diào)用后退出全屏
? private Task ToggleFullScreen() => await JSRuntime.InvokeVoidAsync("bb_toggleFullscreen");
}
再次進(jìn)行思考,如果將一顆按鈕封裝為組件,那只有UI界面才能調(diào)用,而且式樣什么的都不算最靈活,為何不做成一個(gè)服務(wù),與UI分開解耦呢? 別著急, 馬上開干.
我作為一個(gè)blazor愛好者,每一個(gè)想法,轉(zhuǎn)化為一個(gè)組件后,是值得提交到例如BootstrapBlazor之類組件庫大家一起學(xué)習(xí)一起進(jìn)步的,自從我2020-09-25把ZXingBlazor組件提交到BootstrapBlazor之后,從自嗨到團(tuán)隊(duì)合作,真的學(xué)習(xí)到了很多知識(shí)和技巧,在學(xué)習(xí)BB的源碼的過程中,深刻體會(huì)到了那句話的精髓:"每入一寸就有一寸的驚喜!".
項(xiàng)目負(fù)責(zé)人Argo作為一位微軟MVP和業(yè)內(nèi)人士,對(duì)整個(gè)微軟技術(shù)棧有很深刻的認(rèn)識(shí)和思考,對(duì)我本人更是幫助巨大,在此謝謝Argo, :-)
最后版本代碼已經(jīng)提交為組件庫里面的一個(gè)組件,所以有些代碼繼承了組件庫的功能,如果運(yùn)行跟默認(rèn)Blazor工程有不一致的地方,大家可以Fork到自己倉庫去試驗(yàn),以下文章不再贅述.
構(gòu)建服務(wù)?FullScreenService.cs
using Microsoft.AspNetCore.Components;
namespace BootstrapBlazor.Components;
/// <summary>
/// FullScreen 服務(wù)
/// </summary>
public class FullScreenService : BootstrapServiceBase<FullScreenOption>
{
? ? /// <summary>
? ? /// 全屏方法,已經(jīng)全屏?xí)r再次調(diào)用后退出全屏
? ? /// </summary>
? ? /// <param name="option"></param>
? ? /// <returns></returns>
? ? public Task Toggle(FullScreenOption? option = null) => Invoke(option ?? new());
? ? /// <summary>
? ? /// 通過 ElementReference 將指定元素進(jìn)行全屏
? ? /// </summary>
? ? /// <param name="element"></param>
? ? /// <returns></returns>
? ? public Task ToggleByElement(ElementReference element) => Invoke(new() { Element = element });
? ? /// <summary>
? ? /// 通過元素 Id 將指定元素進(jìn)行全屏
? ? /// </summary>
? ? /// <param name="id"></param>
? ? /// <returns></returns>
? ? public Task ToggleById(string id) => Invoke(new() { Id = id });
}
全屏服務(wù)類?FullScreenOption.cs
using Microsoft.AspNetCore.Components;
namespace BootstrapBlazor.Components;
/// <summary>
/// FullScreen 配置類
/// </summary>
public class FullScreenOption
{
? ? /// <summary>
? ? ///
? ? /// </summary>
? ? public ElementReference Element { get; set; }
? ? /// <summary>
? ? ///
? ? /// </summary>
? ? public string? Id { get; set; }
}
注冊(cè)服務(wù)
services.TryAddScoped<FullScreenService>();
FullScreen.cs
using Microsoft.AspNetCore.Components;
namespace BootstrapBlazor.Components;
/// <summary>
/// FullScreen 組件部分類
/// </summary>
public class FullScreen : BootstrapComponentBase, IDisposable
{
? ? /// <summary>
? ? /// DialogServices 服務(wù)實(shí)例
? ? /// </summary>
? ? [Inject]
? ? [NotNull]
? ? private FullScreenService? FullScreenService { get; set; }
? ? /// <summary>
? ? /// OnInitialized 方法
? ? /// </summary>
? ? protected override void OnInitialized()
? ? {
? ? ? ? base.OnInitialized();
? ? ? ? // 注冊(cè) FullScreen 彈窗事件
? ? ? ? FullScreenService.Register(this, Show);
? ? }
? ? /// <summary>
? ? /// OnAfterRenderAsync 方法
? ? /// </summary>
? ? /// <param name="firstRender"></param>
? ? /// <returns></returns>
? ? protected override async Task OnAfterRenderAsync(bool firstRender)
? ? {
? ? ? ? await base.OnAfterRenderAsync(firstRender);
? ? ? ? if (Option != null)
? ? ? ? {
? ? ? ? ? ? await JSRuntime.InvokeVoidAsync(Option.Element.Context != null ? Option.Element : "", "bb_toggleFullscreen", Option.Id ?? "");
? ? ? ? ? ? Option = null;
? ? ? ? }
? ? }
? ? private FullScreenOption? Option { get; set; }
? ? private Task Show(FullScreenOption option)
? ? {
? ? ? ? Option = option;
? ? ? ? StateHasChanged();
? ? ? ? return Task.CompletedTask;
? ? }
? ? /// <summary>
? ? /// Dispose 方法
? ? /// </summary>
? ? /// <param name="disposing"></param>
? ? protected virtual void Dispose(bool disposing)
? ? {
? ? ? ? if (disposing)
? ? ? ? {
? ? ? ? ? ? FullScreenService.UnRegister(this);
? ? ? ? }
? ? }
? ? /// <summary>
? ? /// Dispose 方法
? ? /// </summary>
? ? public void Dispose()
? ? {
? ? ? ? Dispose(disposing: true);
? ? ? ? GC.SuppressFinalize(this);
? ? }
}
組件方式調(diào)用 [簡(jiǎn)化版]?FullScreenButton.Razor
@namespace BootstrapBlazor.Components
@inherits TooltipComponentBase
<a @attributes="AdditionalAttributes" id="@Id" class="@ClassString" @onclick="ToggleFullScreen">
? ? <i class="@ButtonIconString"></i>
? ? <i class="@FullScreenIconString"></i>
</a>
<CascadingValue Value="this" IsFixed="true">
? ? <Tooltip Title="@Title" />
</CascadingValue>
@code{
? ? [Inject]
? ? [NotNull]
? ? private FullScreenService? FullScrenService { get; set; }
? ? private Task ToggleFullScreen() => FullScrenService.Toggle();
}
Razor
@page "/fullscreens"
@inject IStringLocalizer<FullScreens> Localizer
<h3>@Localizer["Title"]</h3>
<h4>@((MarkupString)Localizer["H1"].Value)</h4>
<DemoBlock Title="@Localizer["Block1Title"]" Introduction="@Localizer["Block1Intro"]" Name="Normal">
? ? <Button Text="@Localizer["ButtonText1"]" OnClick="ToggleFullScreen"></Button>
</DemoBlock>
<DemoBlock Title="@Localizer["Block2Title"]" Introduction="@Localizer["Block2Intro"]" Name="Title">
? ? <ul class="ul-demo mb-3">
? ? ? ? <li>@((MarkupString)Localizer["Li1"].Value)</li>
? ? ? ? <li>@((MarkupString)Localizer["Li2"].Value)</li>
? ? </ul>
? ? <FullScreenButton Title="@Localizer["Button1Text"]" FullScreenIcon="fa fa-fa" />
? ? <Pre class="mt-3"><@Localizer["Pre"]" /></Pre>
</DemoBlock>
cs代碼
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components;
namespace BootstrapBlazor.Shared.Samples;
/// <summary>
/// FullScreens 全屏示例代碼
/// </summary>
public partial class FullScreens
{
? ? [Inject]
? ? [NotNull]
? ? private FullScreenService? FullScreenService { get; set; }
? ? private async Task ToggleFullScreen()
? ? {
? ? ? ? await FullScreenService.Toggle();
? ? }
}
添加對(duì)應(yīng)組件中文資源到?BootstrapBlazor.Shared/Locales/zh.json?文件
"BootstrapBlazor.Shared.Pages.Coms": {
? ...
? "FullScreenText": "全屏組件 FullScreen",
? ...
},
"BootstrapBlazor.Shared.Samples.FullScreens": {
? "Title": "FullScreen 全屏",
? "Block1Title": "基本用法",
? ...
}
添加對(duì)應(yīng)組件中文資源到?BootstrapBlazor.Shared/Locales/en.json?文件
"BootstrapBlazor.Shared.Pages.Coms": {
? ...
? "FullScreenText": "FullScreen",
? ...
},
"BootstrapBlazor.Shared.Samples.FullScreens": {
? "Title": "FullScreen",
? "Block1Title": "Basic usage",
? ...
}
添加示例到"組件"?頁面
BootstrapBlazor.Shared/Pages/Coms.razor文件,找到某個(gè)組件大類別,例如導(dǎo)航組件?<ComponentCategory Text="@Localizer["Text2"]">
<ComponentCategory Text="@Localizer["Text2"]">
? ...
? <ComponentCard Text="@Localizer["FullScreenText"]" Image="FullScreen.jpg" Url="fullscreens"></ComponentCard>
? ...
</ComponentCategory>
BootstrapBlazor.Shared/docs.json添加
"fullscreens": "FullScreens",
NavMenu.razor
? ? private void AddNavigation(DemoMenuItem item)
? ? {
? ? ? ? item.Items = new List<DemoMenuItem>
? ? ? ? {
? ? ? ? ? ? ...
? ? ? ? ? ? new()
? ? ? ? ? ? {
? ? ? ? ? ? ? ? IsNew = true,
? ? ? ? ? ? ? ? Text = Localizer["FullScreen"],
? ? ? ? ? ? ? ? Url = "fullscreens"
? ? ? ? ? ? },
? ? ? ? ? ? ...
? ? ? ? };
? ? ? ? AddBadge(item);
? ? }
示例文件?BootstrapBlazor.Shared/Samples/FullScreens.razor
@page "/fullscreens"
@inject IStringLocalizer<FullScreens> Localizer
<h3>@Localizer["Title"]</h3>
<DemoBlock Title="@Localizer["Block1Title"]" Introduction="@Localizer["Block1Intro"]" Name="Normal">
? ? <Button Text="@Localizer["ButtonText1"]" OnClick="ToggleFullScreen"></Button>
</DemoBlock>
<DemoBlock Title="@Localizer["Block2Title"]" Introduction="@Localizer["Block2Intro"]" Name="Title">
? ? <FullScreenButton Title="@Localizer["Button1Text"]" FullScreenIcon="fa fa-fa" />
</DemoBlock>
示例文件?BootstrapBlazor.Shared/Samples/FullScreens.razor.cs
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components;
namespace BootstrapBlazor.Shared.Samples;
/// <summary>
/// FullScreens 全屏示例代碼
/// </summary>
public partial class FullScreens
{
? ? [Inject]
? ? [NotNull]
? ? private FullScreenService? FullScreenService { get; set; }
? ? private async Task ToggleFullScreen()
? ? {
? ? ? ? await FullScreenService.Toggle();
? ? }
}
ASP.NET Core Blazor?https://docs.microsoft.com/zh-cn/aspnet/core/blazor/?view=aspnetcore-6.0?五分鐘了解 Blazor?https://segmentfault.com/a/1190000040800253?Element.requestFullscreen()?https://developer.mozilla.org/zh-CN/docs/Web/API/Element/requestFullScreen?Bootstrap 風(fēng)格的 Blazor UI 組件庫?https://www.blazor.zone/index?!1821 feat(#I48WXD): add FullScreen component?https://gitee.com/LongbowEnterprise/BootstrapBlazor/commit/30caa995eba38e91d15b8a5465c6c9c738db068f