基于.NetCore开发博客项目 StarBlog - (14) 实现主题切换功能
系列文章
- 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客?
- 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目
- 基于.NetCore开发博客项目 StarBlog - (3) 模型设计
- 基于.NetCore开发博客项目 StarBlog - (4) markdown博客批量导入
- 基于.NetCore开发博客项目 StarBlog - (5) 开始搭建Web项目
- 基于.NetCore开发博客项目 StarBlog - (6) 页面开发之博客文章列表
- 基于.NetCore开发博客项目 StarBlog - (7) 页面开发之文章详情页面
- 基于.NetCore开发博客项目 StarBlog - (8) 分类层级结构展示
- 基于.NetCore开发博客项目 StarBlog - (9) 图片批量导入
- 基于.NetCore开发博客项目 StarBlog - (10) 图片瀑布流
- 基于.NetCore开发博客项目 StarBlog - (11) 实现访问统计
- 基于.NetCore开发博客项目 StarBlog - (12) Razor页面动态编译
- 基于.NetCore开发博客项目 StarBlog - (13) 加入友情链接功能
- 基于.NetCore开发博客项目 StarBlog - (14) 实现主题切换功能
- ...
前言
近期一直在写代码,给博客新增了一些功能,本来想写一篇文章一起介绍的,不过好像篇幅会太长,想想还是分开写好了~
本文介绍主题切换功能,StarBlog博客页面基于Bootstrap5实现,Bootstrap本身的设计是比较简洁大方的,不过看久了也会腻,自己调css太麻烦,好在Bootstrap世界有个东西叫bootswatch,提供了几十套主题,我只需要npm install
然后就可以实现主题切换功能了~
实现效果
照例先看看实现的效果
默认主题 | quartz主题 |
---|---|
![]() |
![]() |
PS:目前浅色主题可以比较好的适配,深色主题还在适配中,部分页面的显示效果不佳
思路
bootswatch切换主题的方式是引入其提供的bootstrap.css
文件,覆盖Bootstrap默认的样式
我在DjangoStarter中实现的切换主题用的是Django的TemplateTag
,它可以在模板渲染的时候根据用户选择的主题,加入对应的css文件引用
理论上使用AspNetCore MVC实现的StarBlog项目也是可以用这种方式实现主题切换的,不过当时开发时遇到一些处理起来比较麻烦的问题,所以我决定改为暴露主题API,通过JS动态加载css的方式实现。
添加依赖
开始代码~
首先添加bootswatch
依赖,需要和Bootstrap版本对应,本项目使用的Bootstrap版本是5.1.3,所以这个bootswatch版本也需要同步使用5.1.3
yarn add bootswatch
在gulpfile.js
中配置自动复制
//使用 npm 下载的前端组件包
const libs = [
{name: "bootswatch", dist: "./node_modules/bootswatch/dist/**/*.*"},
];
在StarBlog.Web
目录下执行gulp move
命令,gulp会自动把bootswatch相关文件复制到wwwroot/lib
目录中,方便接下来的使用
关于使用NPM和Gulp管理静态资源的详情,可以参考前面的这篇文章:Asp-Net-Core开发笔记:使用NPM和gulp管理前端静态文件
编写Service
在StarBlog.Web/Services
中添加ThemeService.cs
首先是定义Theme模型
public class Theme {
public string Name { get; set; }
public string Path { get; set; }
public string CssUrl { get; set; }
}
然后ThemeService
,扫描wwwroot/lib/bootswatch
中的所有主题,同时把Bootstrap默认主题也加入
public class ThemeService {
public const string BootstrapTheme = "Bootstrap";
private const string CssUrlPrefix = "/lib/bootswatch/dist";
public List<Theme> Themes { get; set; } = new() {
new Theme {Name = BootstrapTheme, Path = "", CssUrl = ""}
};
public ThemeService(IWebHostEnvironment env) {
var themePath = Path.Combine(env.WebRootPath, "lib", "bootswatch", "dist");
foreach (var item in Directory.GetDirectories(themePath)) {
var name = Path.GetFileName(item);
Themes.Add(new Theme {
Name = name,
Path = item,
CssUrl = $"{CssUrlPrefix}/{name}/bootstrap.min.css"
});
}
}
}
然后注册为单例服务就OK了
builder.Services.AddSingleton<ThemeService>();
写个接口
然后还需要写一个接口
在StarBlog.Web/Apis
目录下新增个ThemeController.cs
,代码很简单,只有一个action,获取全部主题
/// <summary>
/// 页面主题
/// </summary>
[ApiController]
[Route("Api/[controller]")]
[ApiExplorerSettings(GroupName = "common")]
public class ThemeController : ControllerBase {
private readonly ThemeService _themeService;
public ThemeController(ThemeService themeService) {
_themeService = themeService;
}
[HttpGet]
public List<Theme> GetAll() {
return _themeService.Themes;
}
}
前端实现
主题的后端部分完成了,前端需要完成三部分功能
- 请求接口,获取全部主题列表
- 设置主题,动态引入css
- 保存当前选择的主题,下次打开页面时自动引入
为了方便DOM操作,我使用了Vue,在Views/Shared/_Layout.cshtml
底部引入
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/lib/vue/dist/vue.js"></script>
<script src="~/js/site.js"></script>
先在来写最后一个引入的site.js
代码,使用vue,网页打开时通过fetch函数加载主题列表,然后显示在页面上
还有切换主题时将当前主题的名称和css链接保存在localStorage中,下次加载页面的时候可以自动引入
let app = new Vue({
el: '#vue-header',
data: {
currentTheme: '',
themes: []
},
created: function () {
fetch('/Api/Theme')
.then(res => res.json())
.then(res => {
this.themes = res.data
})
// 读取本地主题配置
let theme = localStorage.getItem('currentTheme')
if (theme != null) this.currentTheme = theme
},
methods: {
setTheme(themeName) {
let theme = this.themes.find(t => t.name === themeName)
loadStyles(theme.cssUrl)
this.currentTheme = themeName
localStorage.setItem('currentTheme', themeName)
localStorage.setItem('currentThemeCssUrl', theme.cssUrl)
// 换主题之后最好要刷新页面,不然可能样式冲突
location.reload()
}
}
})
这里加载了主题,通过vue的双向绑定,把主题渲染在顶部菜单上(同时高亮当前主题)
也就是这个地方
<div class="px-3 py-2 border-bottom mb-3">
<div class="container d-flex flex-wrap justify-content-center">
<form class="col-12 col-lg-auto mb-2 mb-lg-0 me-lg-auto" asp-controller="Search" asp-action="Blog">
<input type="search" class="form-control" placeholder="Search..." aria-label="Search" name="keyword">
</form>
<div class="text-end">
<span class="dropdown me-2">
<a class="btn btn-secondary dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-bs-toggle="dropdown" aria-expanded="false">
Themes
</a>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuLink">
<li v-for="theme in themes">
<a v-if="theme.name===currentTheme" class="dropdown-item active">{{theme.name}}</a>
<a v-else class="dropdown-item" v-on:click="setTheme(theme.name)">{{theme.name}}</a>
</li>
</ul>
</span>
</div>
</div>
</div>
最后,还需要在页面刷新的时候读取主题配置,然后自动加载当前主题
因为动态切换主题会导致一些样式冲突啥的,所以需要在页面还没加载完成的时候先引入
因此我又写了个site.preload.js
,放在页面的<head>
部分
<script src="~/js/site.preload.js"></script>
代码如下,先读取当前主题,如果有设置过主题就读取CSS链接并且引入
(同时前面的site.js
中也有使用到这个loadStyles
函数)
// 动态加载CSS
function loadStyles(url) {
let link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = url;
let head = document.getElementsByTagName("head")[0];
head.appendChild(link);
}
let currentTheme = localStorage.getItem('currentTheme')
if (currentTheme !== 'Bootstrap') {
let themeCssUrl = localStorage.getItem('currentThemeCssUrl')
if (themeCssUrl != null) loadStyles(themeCssUrl)
}
PS:动态加载CSS的代码来自:http://lengyun.github.io/js/3-2-2dynamicAddCSS.html
搞定~
基于.NetCore开发博客项目 StarBlog - (14) 实现主题切换功能的更多相关文章
- 基于.NetCore开发博客项目 StarBlog - (13) 加入友情链接功能
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...
- 基于.NetCore开发博客项目 StarBlog - (15) 生成随机尺寸图片
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...
- 基于.NetCore开发博客项目 StarBlog - (16) 一些新功能 (监控/统计/配置/初始化)
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...
- 基于.NetCore开发博客项目 StarBlog - (17) 自动下载文章里的外部图片
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...
- 基于.NetCore开发博客项目 StarBlog - (18) 实现本地Typora文章打包上传
前言 九月太忙,只更新了三篇文章,本来这个功能是从九月初就开始做的,结果一直拖到现在国庆假期才有时间完善并且写文章~ 之前我更新了几篇关于 Python 的文章,有朋友留言问是不是不更新 .Net 了 ...
- 基于.NetCore开发博客项目 StarBlog - (19) Markdown渲染方案探索
前言 笔者认为,一个博客网站,最核心的是阅读体验. 在开发StarBlog的过程中,最耗时的恰恰也是文章的展示部分功能. 最开始还没研究出来如何很好的使用后端渲染,所以只能先用Editor.md组件做 ...
- 基于.NetCore开发博客项目 StarBlog - (5) 开始搭建Web项目
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...
- 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 ... 基于. ...
- 基于.NetCore开发博客项目 StarBlog - (3) 模型设计
系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...
随机推荐
- 深度剖析text-align家族
大家好,我是半夏,一个刚刚开始写文的沙雕程序员.如果喜欢我的文章,可以关注 点赞 加我微信:frontendpicker,一起学习交流前端,成为更优秀的工程师-关注公众号:搞前端的半夏,了解更多前端知 ...
- 【在下版本,有何贵干?】Dockerfile中 RUN yum -y install vim失败Cannot prepare internal mirrorlist: No URLs in mirrorlist
隐秘的版本问题---- Dockerfile中 RUN yum -y install vim失败Cannot prepare internal mirrorlist: No URLs in mirro ...
- ImageKnife组件,让小白也能轻松搞定图片开发
本期我们给大家带来的是开发者周黎生的分享,希望能给你的HarmonyOS开发之旅带来启发~ 图片是UI界面的重要元素之一, 图片加载速度及效果直接影响应用体验.ArkUI开发框架提供了丰富的图像处理能 ...
- RESTFul是一种风格
只要符合RESTFul风格的,都可以叫做使用了RESTFul架构,一般的网站里传数据,都是用的?a=1&b=2...如果是RESTFul风格的话,就会是/a/1/b/2..类似于这样的方式来传 ...
- 羽夏 MakeFile 简明教程
写在前面 此系列是本人一个字一个字码出来的,包括示例和实验截图.该文章根据 GNU Make Manual 进行汉化处理并作出自己的整理,一是我对 Make 的学习记录,二是对大家学习 MakeF ...
- jQuery操作标签,jQuery事件操作,jQuery动画效果,前端框架
jQuery操作标签 jQuery代码查找标签绑定的变量名推荐使用 $xxxEle 样式类操作 addClass();// 添加指定的CSS类名. removeClass();// 移除指定的CSS类 ...
- NLP教程(7) - 问答系统
作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/36 本文地址:http://www.showmeai.tech/article-det ...
- 那些年你啃过的ConcurrentHashMap
前言 我是fancy,一个年纪轻轻bug量就累计到3200个的程序员,同事们都夸我一个人养活了整个测试组. 最近迷上了并发编程.并发这玩意怎么说呢,就是你平时工作用不到,一用就用在面试上.这不,又卷起 ...
- swagger在线api文档搭建指南,用于线上合适么?
在上一篇文章中,我们讲解了什么是 api,什么是 sdk: https://www.cnblogs.com/tanshaoshenghao/p/16217608.html 今天将来到我们万丈高楼平地起 ...
- VMware 虚拟机图文安装和配置 AlmaLinux OS 8.6 教程
前言: 这是<VMware 虚拟机图文安装和配置 Rocky Linux 8.5 教程>一文的姐妹篇教程,如果你需要阅读它,请点击这里. 2020 年,CentOS 宣布:计划未来将重心从 ...