Blazor 簡介
Blazor 是一個使用 .NET 生成的交互式客戶端 Web UI 的框架。和前端同學所熟知的 Vue、React、Angular 有巨大差異。
其最大的特色是使用 C# 代碼(理論上可以是 .NET 生態的任何語言)代替 JavaScript 來實現邏輯。
- 使用 C# 代替 JavaScript 來創建信息豐富的交互式 UI。
- 共享使用 .NET 編寫的服務器端和客戶端應用邏輯。
- 將 UI 呈現為 HTML 和 CSS,以支持眾多瀏覽器,其中包括移動瀏覽器。
- 與新式托管平台(如 Docker)集成。
- 使用 .NET 和 Blazor 生成混合桌面和移動應用。
使用 .NET 進行客戶端 Web 開發可提供以下優勢:
- 使用 C# 代替 JavaScript 來編寫代碼。
- 利用現有的 .NET 庫生態系統。
- 在服務器和客戶端之間共享應用邏輯。
- 受益於 .NET 的性能、可靠性和安全性。
- 使用開發環境(例如 Visual Studio 或 Visual Studio Code)保持 Windows、Linux 或 macOS 上的工作效率。
- 以一組穩定、功能豐富且易用的通用語言、框架和工具為基礎來進行生成。
有兩種不同開發模式
Blazor WebAssembly, C# 代碼運行在瀏覽器中。
Blazor Server,C# 代碼在服務器端執行,使用 SignalR 同步到瀏覽器進行更新。
Blazor 涉及技術
Blazor 是 apt.net core 生態的組成部分,所涉及到的技術也大部分和 .net 相關。
視圖層,使用 Razor 3 技術進行前端的編排渲染。Razor是一種標記語法,是 asp.net core 的默認視圖語法,最顯著的特點是強類型(C#、VB等)的代碼可以和 HTML 寫在一個文件中,當然也可以分開。在 razor 文件中,@符號后面的都是強類型語言,可以是一行中的一部分,也可以是一整行,還可以是一個段落。在 asp.net core 中的大致做法是把 VB、C# 等強類型語言嵌入到網頁,當網頁被請求的時候,在服務器端執行嵌入的代碼,動態生成頁面。
以 Blazor WebAssembly 開發方式運行時,依賴 WebAssembly 4 技術,可以做成靜態頁面不依賴任何后端服務器。
以 Blazor Server 方式開發運行時,依賴 SignalR 5 技術,並且需要后端服務器端配合。
Bootstrap 風格的 Blazor UI 組件庫 - BootstrapBlazor
基於 Bootstrap 樣式庫精心打造,並且額外增加了 100 多種常用的組件,為您快速開發項目帶來非一般的感覺.
Element.requestFullscreen()
參考資料 https://developer.mozilla.org/zh-CN/docs/Web/API/Element/requestFullScreen
Element.requestFullscreen() 方法用於發出異步請求使元素進入全屏模式。
調用此API並不能保證元素一定能夠進入全屏模式。如果元素被允許進入全屏幕模式,返回的Promise會resolve,並且該元素會收到一個fullscreenchange (en-US)事件,通知它已經進入全屏模式。如果全屏請求被拒絕,返回的promise會變成rejected並且該元素會收到一個fullscreenerror (en-US)事件。如果該元素已經從原來的文檔中分離,那么該文檔將會收到這些事件。
早期的Fullscreen API實現總是會把這些事件發送給document,而不是調用的元素,所以你自己可能需要處理這樣的情況。參考 Browser compatibility in [Page not yet written] 來得知哪些瀏覽器做了這個改動。
注意:這個方法只能在用戶交互或者設備方向改變的時候調用,否則將會失敗。
語法
var Promise = Element.requestFullscreen(options);
參數
options 可選
一個FullscreenOptions (en-US)對象提供切換到全屏模式的控制選項。目前,唯一的選項是navigationUI (en-US),這控制了是否在元素處於全屏模式時顯示導航條UI。默認值是"auto",表明這將由瀏覽器來決定是否顯示導航條。
返回值
在完成切換全屏模式后,返回一個已經用值 undefined resolved的Promise
異常
requestFullscreen() 通過拒絕返回的 Promise來生成錯誤條件,而不是拋出一個傳統的異常。拒絕控制器接收以下的某一個值:
TypeError
在以下幾種情況下,會拋出TypeError:
文檔中包含的元素未完全激活,也就是說不是當前活動的元素。
元素不在文檔之內。
因為功能策略限制配置或其他訪問控制,元素不被允許使用"fullscreen"功能。
元素和它的文檔是同一個節點。
初步構建組件
1.建立js腳本
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();
}
}
});
2.建立Razor頁面測試
以下為簡化代碼,運行測試一下功能是否達到需求.
<button @onclick="FullScreen">全屏</button>
<button @onclick="ExitFullScreen">退出全屏</button>
@code{
[Inject] IJSRuntime? JSRuntime{ get; set; }
//進入全屏
private Task FullScreen() => await JSRuntime.InvokeVoidAsync("bb_Fullscreen");
//退出全屏
private Task ExitFullScreen() => await JSRuntime.InvokeVoidAsync("bb_ExitFullscreen");
}
3.優化邏輯,添加單按鈕全屏切換邏輯,添加針對單獨元素的全屏邏輯
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);
測試功能
<button @onclick="ToggleFullScreen">全屏</button>
@code{
[Inject] IJSRuntime? JSRuntime{ get; set; }
//全屏方法,已經全屏時再次調用后退出全屏
private Task ToggleFullScreen() => await JSRuntime.InvokeVoidAsync("bb_toggleFullscreen");
}
4.封裝為服務
再次進行思考,如果將一顆按鈕封裝為組件,那只有UI界面才能調用,而且式樣什么的都不算最靈活,為何不做成一個服務,與UI分開解耦呢? 別着急, 馬上開干.
我作為一個blazor愛好者,每一個想法,轉化為一個組件后,是值得提交到例如BootstrapBlazor之類組件庫大家一起學習一起進步的,自從我2020-09-25把ZXingBlazor組件提交到BootstrapBlazor之后,從自嗨到團隊合作,真的學習到了很多知識和技巧,在學習BB的源碼的過程中,深刻體會到了那句話的精髓:"每入一寸就有一寸的驚喜!".
項目負責人Argo作為一位微軟MVP和業內人士,對整個微軟技術棧有很深刻的認識和思考,對我本人更是幫助巨大,在此謝謝Argo, 😃
最后版本代碼已經提交為組件庫里面的一個組件,所以有些代碼繼承了組件庫的功能,如果運行跟默認Blazor工程有不一致的地方,大家可以Fork到自己倉庫去試驗,以下文章不再贅述.
構建服務 FullScreenService.cs
using Microsoft.AspNetCore.Components;
namespace BootstrapBlazor.Components;
/// <summary>
/// FullScreen 服務
/// </summary>
public class FullScreenService : BootstrapServiceBase<FullScreenOption>
{
/// <summary>
/// 全屏方法,已經全屏時再次調用后退出全屏
/// </summary>
/// <param name="option"></param>
/// <returns></returns>
public Task Toggle(FullScreenOption? option = null) => Invoke(option ?? new());
/// <summary>
/// 通過 ElementReference 將指定元素進行全屏
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
public Task ToggleByElement(ElementReference element) => Invoke(new() { Element = element });
/// <summary>
/// 通過元素 Id 將指定元素進行全屏
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public Task ToggleById(string id) => Invoke(new() { Id = id });
}
全屏服務類 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; }
}
注冊服務
services.TryAddScoped<FullScreenService>();
FullScreen.cs
using Microsoft.AspNetCore.Components;
namespace BootstrapBlazor.Components;
/// <summary>
/// FullScreen 組件部分類
/// </summary>
public class FullScreen : BootstrapComponentBase, IDisposable
{
/// <summary>
/// DialogServices 服務實例
/// </summary>
[Inject]
[NotNull]
private FullScreenService? FullScreenService { get; set; }
/// <summary>
/// OnInitialized 方法
/// </summary>
protected override void OnInitialized()
{
base.OnInitialized();
// 注冊 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);
}
}
組件方式調用 [簡化版] 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();
}
5.FullScreens 全屏示例代碼
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();
}
}
最終版本


BootstrapBlazor提交組件簡單步驟

示例文檔
- 添加對應組件中文資源到
BootstrapBlazor.Shared/Locales/zh.json文件
"BootstrapBlazor.Shared.Pages.Coms": {
...
"FullScreenText": "全屏組件 FullScreen",
...
},
"BootstrapBlazor.Shared.Samples.FullScreens": {
"Title": "FullScreen 全屏",
"Block1Title": "基本用法",
...
}
- 添加對應組件中文資源到
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文件,找到某個組件大類別,例如導航組件<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 風格的 Blazor UI 組件庫 https://www.blazor.zone/index
!1821 feat(#I48WXD): add FullScreen component https://gitee.com/LongbowEnterprise/BootstrapBlazor/commit/30caa995eba38e91d15b8a5465c6c9c738db068f
項目源碼
關聯項目
BA & Blazor QQ群:795206915、675147445
知識共享許可協議
本作品采用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。歡迎轉載、使用、重新發布,但務必保留文章署名AlexChow(包含鏈接: https://github.com/densen2014 ),不得用於商業目的,基於本文修改后的作品務必以相同的許可發布。如有任何疑問,請與我聯系 。
