上一篇搭建了 Blazor 項目並將總體框架改造了一下,本篇將完成用 C# 代碼代替 JavaScript 實現幾個小功能,說是代替但並不能徹底不用 JavaScript,應該說是儘可能不用吧。javascript
能夠看到,當我鼠標移入的時候顯示二維碼,移出的時候隱藏二維碼。css
這個功能若是是用JavaScript來完成的話,確定首先想到的是HTML的 Mouse 事件屬性,那麼在Blazor中也是同樣的,給咱們實現了各類on*
事件。html
打開index.razor
頁面,給微信圖標那個 NavLink 標籤添加兩個事件,@onmouseover
和@onmouseout
。java
... <NavLink class="link-item weixin" title="掃碼關注微信公衆號:『阿星Plus』查看更多。" @onmouseover="Hover" @onmouseout="Hover"> <i class="iconfont iconweixin"></i> </NavLink> ...
當鼠標移入移出的時候都執行咱們自定義的一個方法Hover()
。git
C# 代碼寫在@code{}
花括號中,實現顯示和隱藏原理是利用css,默認是隱藏的,當顯示的時候將具備隱藏屬性的class值去掉就能夠了。github
因此,能夠添加兩個字段,一個用於判斷當前是否處於隱藏狀態,一個用來存儲class的值。web
/// <summary> /// 是否隱藏 /// </summary> private bool IsHidden = true; /// <summary> /// 二維碼CSS /// </summary> private string QrCodeCssClass => IsHidden ? "hidden" : null;
當IsHidden = true
,QrCodeCssClass = "hidden"
,當IsHidden = false
,QrCodeCssClass = null
。api
那麼在Hover()
方法中,不斷修改IsHidden
的值就能夠實現效果了。瀏覽器
/// <summary> /// 鼠標移入移出操做 /// </summary> private void Hover() => IsHidden = !IsHidden;
最後將QrCodeCssClass
變量賦值給二維碼圖片所在的div上。緩存
... <div class="qrcode @QrCodeCssClass"> <img src="https://static.meowv.com/images/wx_qrcode.jpg" /> </div> ...
大功告成,index.razor
完整代碼以下:
@page "/" <div class="main"> <div class="container"> <div class="intro"> <div class="avatar"> <a href="javascript:;"><img src="https://static.meowv.com/images/avatar.jpg"></a> </div> <div class="nickname">阿星Plus</div> <div class="description"> <p> 生命不息,奮鬥不止 <br>Cease to struggle and you cease to live </p> </div> <div class="links"> <NavLink class="link-item" title="Posts" href="posts"> <i class="iconfont iconread"></i> </NavLink> <NavLink target="_blank" class="link-item" title="Notes" href="https://notes.meowv.com/"> <i class="iconfont iconnotes"></i> </NavLink> <NavLink target="_blank" class="link-item" title="API" href="https://api.meowv.com/"> <i class="iconfont iconapi"></i> </NavLink> <NavLink class="link-item" title="Manage" href="/account/auth"> <i class="iconfont iconcode"></i> </NavLink> <NavLink target="_blank" class="link-item" title="Github" href="https://github.com/Meowv/"> <i class="iconfont icongithub"></i> </NavLink> <NavLink class="link-item weixin" title="掃碼關注微信公衆號:『阿星Plus』查看更多。" @onmouseover="Hover" @onmouseout="Hover"> <i class="iconfont iconweixin"></i> </NavLink> <div class="qrcode @QrCodeCssClass"> <img src="https://static.meowv.com/images/wx_qrcode.jpg" /> </div> </div> </div> </div> </div> @code { /// <summary> /// 是否隱藏 /// </summary> private bool IsHidden = true; /// <summary> /// 二維碼CSS /// </summary> private string QrCodeCssClass => IsHidden ? "hidden" : null; /// <summary> /// 鼠標移入移出操做 /// </summary> private void Hover() => IsHidden = !IsHidden; }
菜單是在小屏幕上纔會出現的,相信看完了二維碼的顯示與隱藏,這個菜單的顯示與隱藏就好辦了吧,實現方法是同樣的,菜單按鈕是在頭部組件Header.razor
中的,包括主題切換功能,因此下面代碼都在Header.razor
裏面。
@code { /// <summary> /// 下拉菜單是否打開 /// </summary> private bool collapseNavMenu = false; /// <summary> /// 導航菜單CSS /// </summary> private string NavMenuCssClass => collapseNavMenu ? "active" : null; /// <summary> /// 顯示/隱藏 菜單 /// </summary> private void ToggleNavMenu() => collapseNavMenu = !collapseNavMenu; }
默認是不打開的,collapseNavMenu = false
。而後根據collapseNavMenu
值爲NavMenuCssClass
給定不一樣的class。
... <nav class="navbar-mobile"> <div class="container"> <div class="navbar-header"> <div> <NavLink class="menu-item" href="" Match="NavLinkMatch.All">😍阿星Plus</NavLink> <NavLink> · Light</NavLink> </div> <div class="menu-toggle" @onclick="ToggleNavMenu">☰ Menu</div> </div> <div class="menu @NavMenuCssClass"> <NavLink class="menu-item" href="posts">Posts</NavLink> <NavLink class="menu-item" href="categories">Categories</NavLink> <NavLink class="menu-item" href="tags">Tags</NavLink> <NavLink class="menu-item apps" href="apps">Apps</NavLink> </div> </div> </nav> ...
與二維碼顯示與隱藏惟一區別就是這裏是點擊按鈕,不是移入移出,因此菜單顯示與隱藏須要用到@onclick
方法。
哇,這個主題切換真的是一言難盡,當切換主題的時候須要記住當前的主題是什麼,當刷新頁面或者跳轉其餘頁面的時候,主題狀態是須要一致的,默認是白色主題,當切換暗黑色主題後實際上是在body上加了一個class。
在Blazor實在是不知道用什麼辦法去動態控制body的樣式,因此這裏我想到了一個辦法,寫幾個全局的JavaScript方法,而後再Blazor中調用,要知道,他們是能夠互相調用的,因而問題迎刃而解。
添加app.js
文件,放在 /wwwroot/js/ 下面。
var func = window.func || {}; func = { setStorage: function (name, value) { localStorage.setItem(name, value); }, getStorage: function (name) { return localStorage.getItem(name); }, switchTheme: function () { var currentTheme = this.getStorage('theme') || 'Light'; var isDark = currentTheme === 'Dark'; if (isDark) { document.querySelector('body').classList.add('dark-theme'); } else { document.querySelector('body').classList.remove('dark-theme'); } } };
這裏寫了三個方法,設置localStorage:setStorage(name,value)
,獲取localStorage:getStorage(name)
,切換主題:switchTheme()
,localStorage 是瀏覽器以 name:value 形式的本地存儲對象。
switchTheme
主要作的事情就是,判斷當前主題若是是暗黑,就給body加上對應的class,若是不是就去掉。
而後在 index.html 中引用。
... <body> <app> <div class="loader"></div> </app> <script src="js/app.js"></script> <script src="_framework/blazor.webassembly.js"></script> </body> ...
有了這個三個全局的JavaScript方法,切換主題就變得簡單多了,看代碼。
... /// <summary> /// 當前主題 /// </summary> private string currentTheme; /// <summary> /// 初始化 /// </summary> /// <returns></returns> protected override async Task OnInitializedAsync() { currentTheme = await JSRuntime.InvokeAsync<string>("window.func.getStorage", "theme") ?? "Light"; await JSRuntime.InvokeVoidAsync("window.func.switchTheme"); } ...
注意在Blazor調用JavaScript方法須要注入IJSRuntime
接口,@inject IJSRuntime JSRuntime
。
新建一個變量currentTheme
,在生命週期函數初始化的時候去調用JavaScript中的getStorage
方法,獲取當前主題,考慮到第一次訪問的狀況,能夠給一個默認值爲Light,表示白色主題,而後再去調用switchTheme,執行切換主題的方法。這樣頁面就會根據localStorage
的值來肯定當前的主題。
... /// <summary> /// 切換主題 /// </summary> private async Task SwitchTheme() { currentTheme = currentTheme == "Light" ? "Dark" : "Light"; await JSRuntime.InvokeVoidAsync("window.func.setStorage", "theme", currentTheme); await JSRuntime.InvokeVoidAsync("window.func.switchTheme"); } ...
SwitchTheme()
是切換主題的方法,當咱們點擊input按鈕時能夠任意切換,而且主題還要實時跟着變化。
當點擊按鈕執行SwitchTheme()
時候改變currentTheme
的值,而後將currentTheme
傳遞給JavaScript方法setStorage
,最後再次執行切換主題的JavaScript方法便可。
此時變量currentTheme
也發揮了很多做用,在小屏幕下會顯示當前主題的名稱,Dark or Light,能夠直接將currentTheme
在HTML中賦值便可。
而且咱們input是checkbox類型,當是黑色主題的時候須要時選中的狀態,白色主題的時候不選中,這裏就能夠利用checked屬性這樣寫:checked="@(currentTheme == "Dark")"
。
<nav class="navbar"> <div class="container"> ... <div class="menu navbar-right"> ... <input id="switch_default" type="checkbox" class="switch_default" @onchange="SwitchTheme" checked="@(currentTheme == "Dark")" /> <label for="switch_default" class="toggleBtn"></label> </div> </div> </nav> <nav class="navbar"> <div class="container"> ... <div class="menu navbar-right"> ... <input id="switch_default" type="checkbox" class="switch_default" @onchange="SwitchTheme" checked="@(currentTheme == "Dark")" /> <label for="switch_default" class="toggleBtn"></label> </div> </div> </nav> <nav class="navbar-mobile"> <div class="container"> <div class="navbar-header"> <div> <NavLink class="menu-item" href="" Match="NavLinkMatch.All">😍阿星Plus</NavLink> <NavLink @onclick="SwitchTheme"> · @currentTheme</NavLink> </div> <div class="menu-toggle" @onclick="ToggleNavMenu">☰ Menu</div> </div> <div class="menu @NavMenuCssClass"> ... </div> </div> </nav>
OK,搞定,快去試試吧。
如今看起來亂亂的,而且設置獲取localStorage
屬於公共的方法,說不定之後也能用到,咱們將其封裝一下,便於往後的調用,否則要寫好多重複的代碼。
在Blazor項目根目錄添加文件夾Commons,在文件夾下添加一個Common.cs
,目前用到了IJSRuntime
,用構造函數注入,而後寫幾個公共的方法。
//Common.cs using Microsoft.JSInterop; using System.Threading.Tasks; namespace Meowv.Blog.BlazorApp.Commons { public class Common { private readonly IJSRuntime _jsRuntime; public Common(IJSRuntime jsRuntime) { _jsRuntime = jsRuntime; } /// <summary> /// 執行無返回值方法 /// </summary> /// <param name="identifier"></param> /// <param name="args"></param> /// <returns></returns> public async ValueTask InvokeAsync(string identifier, params object[] args) { await _jsRuntime.InvokeVoidAsync(identifier, args); } /// <summary> /// 執行帶返回值的方法 /// </summary> /// <typeparam name="TValue"></typeparam> /// <param name="identifier"></param> /// <param name="args"></param> /// <returns></returns> public async ValueTask<TValue> InvokeAsync<TValue>(string identifier, params object[] args) { return await _jsRuntime.InvokeAsync<TValue>(identifier, args); } /// <summary> /// 設置localStorage /// </summary> /// <param name="name"></param> /// <param name="value"></param> /// <returns></returns> public async Task SetStorageAsync(string name, string value) { await InvokeAsync("window.func.setStorage", name, value); } /// <summary> /// 獲取localStorage /// </summary> /// <param name="name"></param> /// <returns></returns> public async Task<string> GetStorageAsync(string name) { return await InvokeAsync<string>("window.func.getStorage", name); } } }
而後須要在Program.cs
中注入。
using Meowv.Blog.BlazorApp.Commons; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.DependencyInjection; using System; using System.Net.Http; using System.Threading.Tasks; namespace Meowv.Blog.BlazorApp { public class Program { public static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add<App>("app"); builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); builder.Services.AddSingleton(typeof(Common)); await builder.Build().RunAsync(); } } }
緊接着在_Imports.razor
中注入使用Common
,@inject Commons.Common Common
。
改造一下Header.razor
,所有代碼以下:
<header> <nav class="navbar"> <div class="container"> <div class="navbar-header header-logo"> <NavLink class="menu-item" href="/" Match="NavLinkMatch.All"> 😍阿星Plus </NavLink> </div> <div class="menu navbar-right"> <NavLink class="menu-item" href="posts">Posts</NavLink> <NavLink class="menu-item" href="categories">Categories</NavLink> <NavLink class="menu-item" href="tags">Tags</NavLink> <NavLink class="menu-item apps" href="apps">Apps</NavLink> <input id="switch_default" type="checkbox" class="switch_default" @onchange="SwitchTheme" checked="@(currentTheme == "Dark")" /> <label for="switch_default" class="toggleBtn"></label> </div> </div> </nav> <nav class="navbar-mobile"> <div class="container"> <div class="navbar-header"> <div> <NavLink class="menu-item" href="" Match="NavLinkMatch.All">😍阿星Plus</NavLink> <NavLink @onclick="SwitchTheme"> · @currentTheme</NavLink> </div> <div class="menu-toggle" @onclick="ToggleNavMenu">☰ Menu</div> </div> <div class="menu @NavMenuCssClass"> <NavLink class="menu-item" href="posts">Posts</NavLink> <NavLink class="menu-item" href="categories">Categories</NavLink> <NavLink class="menu-item" href="tags">Tags</NavLink> <NavLink class="menu-item apps" href="apps">Apps</NavLink> </div> </div> </nav> </header> @code { /// <summary> /// 下拉菜單是否打開 /// </summary> private bool collapseNavMenu = false; /// <summary> /// 導航菜單CSS /// </summary> private string NavMenuCssClass => collapseNavMenu ? "active" : null; /// <summary> /// 顯示/隱藏 菜單 /// </summary> private void ToggleNavMenu() => collapseNavMenu = !collapseNavMenu; /// <summary> /// 當前主題 /// </summary> private string currentTheme; /// <summary> /// 初始化 /// </summary> /// <returns></returns> protected override async Task OnInitializedAsync() { currentTheme = await Common.GetStorageAsync("theme") ?? "Light"; await Common.InvokeAsync("window.func.switchTheme"); } /// <summary> /// 切換主題 /// </summary> private async Task SwitchTheme() { currentTheme = currentTheme == "Light" ? "Dark" : "Light"; await Common.SetStorageAsync("theme", currentTheme); await Common.InvokeAsync("window.func.switchTheme"); } }
實現過程比較簡單,相信你絕對學會了。本篇就到這裏了,未完待續...