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 ),不得用於商業目的,基於本文修改後的作品務必以相同的許可釋出。如有任何疑問,請與我聯絡 。