全球热消息:基于.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
yarnaddbootswatch
在gulpfile.js中配置自动复制
//使用npm下载的前端组件包constlibs=[{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模型
publicclassTheme{publicstringName{get;set;}publicstringPath{get;set;}publicstringCssUrl{get;set;}}
然后ThemeService,扫描wwwroot/lib/bootswatch中的所有主题,同时把Bootstrap默认主题也加入
publicclassThemeService{publicconststringBootstrapTheme="Bootstrap";privateconststringCssUrlPrefix="/lib/bootswatch/dist";publicListThemes{get;set;}=new(){newTheme{Name=BootstrapTheme,Path="",CssUrl=""}};publicThemeService(IWebHostEnvironmentenv){varthemePath=Path.Combine(env.WebRootPath,"lib","bootswatch","dist");foreach(variteminDirectory.GetDirectories(themePath)){varname=Path.GetFileName(item);Themes.Add(newTheme{Name=name,Path=item,CssUrl=$"{CssUrlPrefix}/{name}/bootstrap.min.css"});}}}
然后注册为单例服务就OK了
builder.Services.AddSingleton写个接口();
然后还需要写一个接口
在StarBlog.Web/Apis目录下新增个ThemeController.cs,代码很简单,只有一个action,获取全部主题
///前端实现///页面主题/// [ApiController][Route("Api/[controller]")][ApiExplorerSettings(GroupName="common")]publicclassThemeController:ControllerBase{privatereadonlyThemeService_themeService;publicThemeController(ThemeServicethemeService){_themeService=themeService;}[HttpGet]publicListGetAll(){return_themeService.Themes;}}
主题的后端部分完成了,前端需要完成三部分功能
请求接口,获取全部主题列表设置主题,动态引入css保存当前选择的主题,下次打开页面时自动引入为了方便DOM操作,我使用了Vue,在Views/Shared/_Layout.cshtml底部引入
<scriptsrc="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script><scriptsrc="~/lib/vue/dist/vue.js"></script><scriptsrc="~/js/site.js"></script>
先在来写最后一个引入的site.js代码,使用vue,网页打开时通过fetch函数加载主题列表,然后显示在页面上
还有切换主题时将当前主题的名称和css链接保存在localStorage中,下次加载页面的时候可以自动引入
letapp=newVue({el:"#vue-header",data:{currentTheme:"",themes:[]},created:function(){fetch("/Api/Theme").then(res=>res.json()).then(res=>{this.themes=res.data})//读取本地主题配置lettheme=localStorage.getItem("currentTheme")if(theme!=null)this.currentTheme=theme},methods:{setTheme(themeName){lettheme=this.themes.find(t=>t.name===themeName)loadStyles(theme.cssUrl)this.currentTheme=themeNamelocalStorage.setItem("currentTheme",themeName)localStorage.setItem("currentThemeCssUrl",theme.cssUrl)//换主题之后最好要刷新页面,不然可能样式冲突location.reload()}}})
这里加载了主题,通过vue的双向绑定,把主题渲染在顶部菜单上(同时高亮当前主题)
也就是这个地方
Themes {{theme.name}} {{theme.name}}