基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(二)

系列文章

  1. 使用 abp cli 搭建项目

  2. 给项目瘦身,让它跑起来

  3. 完善与美化,Swagger登场

  4. 数据访问和代码优先

  5. 自定义仓储之增删改查

  6. 统一规范API,包装返回模型

  7. 再说Swagger,分组、描述、小绿锁

  8. 接入GitHub,用JWT保护你的API

  9. 异常处理和日志记录

  10. 使用Redis缓存数据

  11. 集成Hangfire实现定时任务处理

  12. 用AutoMapper搞定对象映射

  13. 定时任务最佳实战(一)

  14. 定时任务最佳实战(二)

  15. 定时任务最佳实战(三)

  16. 博客接口实战篇(一)

  17. 博客接口实战篇(二)

  18. 博客接口实战篇(三)

  19. 博客接口实战篇(四)

  20. 博客接口实战篇(五)

  21. Blazor实战系列(一)


上一篇搭建了 Blazor 项目并将整体框架改造了一下,本篇将完成用 C# 代码代替 JavaScript 实现几个小功能,说是代替但并不能完全不用 JavaScript,应该说是尽量不用吧。

二维码显示与隐藏

可以看到,当我鼠标移入的时候显示二维码,移出的时候隐藏二维码。

这个功能如果是用JavaScript来完成的话,肯定首先想到的是HTML的 Mouse 事件属性,那么在Blazor中也是一样的,给我们实现了各种on*事件。

打开index.razor页面,给微信图标那个 NavLink 标签添加两个事件,@onmouseover@onmouseout

...
<NavLink class="link-item weixin" title="扫码关注微信公众号:『阿星Plus』查看更多。" @onmouseover="Hover" @onmouseout="Hover"><i class="iconfont iconweixin"></i>
</NavLink>
...

当鼠标移入移出的时候都执行我们自定义的一个方法Hover()

C# 代码写在@code{}花括号中,实现显示和隐藏原理是利用css,默认是隐藏的,当显示的时候将具有隐藏属性的class值去掉就可以了。

所以,可以添加两个字段,一个用于判断当前是否处于隐藏状态,一个用来存储class的值。

/// <summary>
/// 是否隐藏
/// </summary>
private bool IsHidden = true;/// <summary>
/// 二维码CSS
/// </summary>
private string QrCodeCssClass => IsHidden ? "hidden" : null;

IsHidden = trueQrCodeCssClass = "hidden",当IsHidden = falseQrCodeCssClass = null

那么在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>&nbsp;·&nbsp;Light</NavLink></div><div class="menu-toggle" @onclick="ToggleNavMenu">&#9776; 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">&nbsp;·&nbsp;@currentTheme</NavLink></div><div class="menu-toggle" @onclick="ToggleNavMenu">&#9776; 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">&nbsp;·&nbsp;@currentTheme</NavLink></div><div class="menu-toggle" @onclick="ToggleNavMenu">&#9776; 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");}
}

实现过程比较简单,相信你绝对学会了。本篇就到这里了,未完待续...

开源地址:https://github.com/Meowv/Blog/tree/blog_tutorial

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309347.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Azure App Service 如何在第一时间用上最新版 .NET Core

点击上方关注“汪宇杰博客” ^_^导语微软会经常对 .NET Core 发布更新&#xff0c;通常为安全补丁。这不&#xff0c;今天早上&#xff0c;.NET Core 3.1.5 更新发布了。然而 Azure App Service 自身的 .NET Core runtime 并不会在第一时间更新&#xff0c;每次都要等几周后微软…

我们是如何做DevOps的?

一、DevOps的理解DevOps的概念理解DevOps 的概念在软件开发行业中逐渐流行起来。越来越多的团队希望实现产品的敏捷开发&#xff0c;DevOps 使一切成为可能。有了 DevOps &#xff0c;团队可以定期发布代码、自动化部署、并将持续集成 / 持续交付作为发布过程的一部分。一句话概…

word文档相关使用

主要是为了记忆&#xff0c;有的时候&#xff0c;之前查阅过&#xff0c;后来使用又忘记了&#xff0c;以后碰了就陆续添加吧&#xff0c;先开一个博文 文章目录插入图片&#xff0c;显示不全的问题&#xff1a;方法一&#xff1a;方法二&#xff1a;方法三&#xff1a;在左侧显…

调试实战 —— dll 加载失败之 Debug Release争锋篇

缘起 最近&#xff0c;项目里遇到一个 dll 加载不上的问题。实际项目比较复杂&#xff0c;但是解决后&#xff0c;又是这么的简单&#xff0c;合情合理。本文是我使用示例工程模拟的&#xff0c;实际项目中另有玄机&#xff0c;但问题的本质是一样的。本文从行文上与 《调试实战…

一文说通Dotnet Core的后台任务

这是一文说通系列的第二篇&#xff0c;里面有些内容会用到第一篇中间件的部分概念。如果需要&#xff0c;可以参看第一篇&#xff1a;一文说通Dotnet Core的中间件一、前言后台任务在一些特殊的应用场合&#xff0c;有相当的需求。比方&#xff0c;我们需要实现一个定时任务、或…

2021年度训练联盟热身训练赛第五场 H题In-place Sorting+贪心构造

题意&#xff1a; 给你n个小于101810^{18}1018的大数&#xff0c;问在可以再不改变序列位置&#xff0c;之改变数值中某数位的‘9’变为‘6’或将‘6’变为‘9’&#xff0c;求的最终序列由小到大&#xff0c;且字典序最小。 题目&#xff1a; 链接&#xff1a;https://ac.n…

用.NET进行客户端Web开发?看这个Bootstrap风格的BlazorUI组件库

点击上方“Dotnet9”添加关注哦Blazor一、前言今天在下班的路上&#xff08;地铁上&#xff09;&#xff0c;站长习惯性的掏出手机&#xff0c;就收到知乎向站长推送的一篇BlazorUI组件库推荐文章&#xff0c;是码云官方的&#xff1a;原文链接[1]&#xff0c;于是我立即打开码…

[JavaWeb-XML]XML约束概述

约束&#xff1a;规定xml文档的书写规则 * 作为框架的使用者(程序员)&#xff1a;1. 能够在xml中引入约束文档2. 能够简单的读懂约束文档* 分类&#xff1a;1. DTD:一种简单的约束技术2. Schema:一种复杂的约束技术

在Asp.NET Core中如何优雅的管理用户机密数据

在Asp.NET Core中如何优雅的管理用户机密数据背景回顾在软件开发过程中&#xff0c;使用配置文件来管理某些对应用程序运行中需要使用的参数是常见的作法。在早期VB/VB.NET时代&#xff0c;经常使用.ini文件来进行配置管理&#xff1b;而在.NET FX开发中&#xff0c;我们则倾向…

基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(三)

系列文章使用 abp cli 搭建项目给项目瘦身&#xff0c;让它跑起来完善与美化&#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API&#xff0c;包装返回模型再说Swagger&#xff0c;分组、描述、小绿锁接入GitHub&#xff0c;用JWT保护你的API异常处理和…

别了,Docker Swarm !你好,K8s !

毫无疑问&#xff0c;Kubernetes已经成为容器编排事实标准。除了已经拥抱Kubernetes的Google、BAT、京东、奇虎360等巨头大厂外&#xff0c;更多的企业也都在向Kubernetes迁移。容器技术大势所趋&#xff0c;是互联网企业目前急需的技术人才之一&#xff0c;已成为运维工程师、…

【翻译】.NET 5 Preview5发布

今天&#xff0c;发布了.NET 5.0 Preview5。主要对它进行了一小部分新功能和性能的改进。.NET 5.0 Preview 4包含了一些计划和.NET 5.0要交付的内容。现在&#xff0c;大多数的功能都已经包含在里面&#xff0c;但是有许多功能还未到最终状态。预计这个版本在Preview 7中完善。…

构造前缀贪心+ 计蒜客 子矩阵求和

题目&#xff1a; 给出一个 nn 行 mm 列的矩阵&#xff0c;矩阵的每个位置有一个非负整数 a[i][j]&#xff0c;有 qq 次询问&#xff0c;每次询问求一个左上角为 (a,b)&#xff0c;右下角为 (c,d) 的子矩阵的所有数之和。 输入格式 第一行两个整数 n,m&#xff0c;表示矩阵的…

[跨平台系列三Docker篇]:ASP.NET Core应用

如果你是老张的忠实读者的话&#xff0c;如果是从博客园就开始看我的文章的话&#xff0c;如果后期也一直看我公众号的话&#xff0c;应该就知道其实我一直在根据一条无形的教学线路来讲解的&#xff0c;&#xff0c;如果你真的是想好好学的话&#xff0c;请好好看看我之前的文…

[壹刊]Azure AD(四)知识补充-服务主体

一&#xff0c;引言又到了新的一周了&#xff0c;也到了我新的分享的时间了&#xff0c;还记得上一周立得Flag&#xff0c;其中 “保证每周输出一篇文章” &#xff0c;让我特别“在意”&#xff08;这里用词不太恰当&#xff09;。主要是我的一个大学舍友&#xff0c;他突然问…

[JavaWeb-Servlet]Servlet_执行原理

执行原理&#xff1a; 1. 当服务器接受到客户端浏览器的请求后&#xff0c;会解析请求URL路径&#xff0c;获取访问的Servlet的资源路径2. 查找web.xml文件&#xff0c;是否有对应的<url-pattern>标签体内容。3. 如果有&#xff0c;则在找到对应的<servlet-class>全…

分享我在前后端分离项目中Gitlab-CI的经验

之前我分享了为ASP.NET Core后端搭建Gitlab-CI/CD实践&#xff0c;今天继续聊一聊为前后端分离搭建Gitlab-CI的额外经验。BeforeGitlab-ci是Gitlab提供的CI/CD特性&#xff0c;结合Gitlab简单友好的配置界面&#xff0c;能愉悦的在Gitlab界面查看管道执行流程&#xff0c;并自然…

lin-cms-dotnetcore.是如何方法级别的权限控制(API级别)的

方法级别的权限控制&#xff08;API级别&#xff09;Lin的定位在于实现一整套 CMS的解决方案&#xff0c;它是一个设计方案&#xff0c;提供了不同的后端&#xff0c;不同的前端&#xff0c;而且也支持不同的数据库目前官方团队维护 lin-cms-vue,lin-cms-spring-boot,lin-cms-k…

基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(四)

系列文章使用 abp cli 搭建项目给项目瘦身&#xff0c;让它跑起来完善与美化&#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API&#xff0c;包装返回模型再说Swagger&#xff0c;分组、描述、小绿锁接入GitHub&#xff0c;用JWT保护你的API异常处理和…

字符串相关

文章目录字符串基础字符串的存储标准库字符串匹配单串匹配多串匹配其他类型的字符串匹配问题字符串哈希Hash 的实现Hash 的分析与改进错误率多次询问子串哈希Hash 的应用字符串匹配允许 k次失配的字符串匹配最长回文子串最长公共子字符串确定字符串中不同子字符串的数量字典树 …