需求背景
A用户:我喜欢亮色!B用户:我喜欢暗色系!...在实际的开发场景中,主题需求一般都是必不可少的,那我们如何简单地实现我们想要的效果呢!
解决方案
话不多说,我们先看一下demo效果
实现思路:在页面的载体上自定义一个属性标签,用于记录切换不同的主题,然后我们的css文件会根据属性标签值的改变而加载不同样式,来实现我们切换主题的效果。干巴巴的描述,理解起来有点晦涩难懂,这里我以自己写的demo为例,和大家一起交流探讨。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
</head>
<body customize-theme-style="dark">
<app-root></app-root>
>
</body>
</html>
目前很多单页面应用都会有一个index.html载体文件,路由<app-root></app-root>
挂载在body
节点上,所以我们的页面组件都是挂载在body
上,那么我们在body
节点上定义一个customize-theme-style="dark"
属性标签用于记录主题状态的切换。接下来就是css会根据主题状态的切换来改变主题样式。
我这里CSS 预处理语言用的是less(如果你用的是sass,或者是其他的css预处理语言,思路都是相同的,语法上有些许不同而已),在资源文件夹里建一个styles用于存放我们的less样式文件,demo的编译环境是angular8.0,所以我们要在angular.json脚手架文件声明我们新建的资源文件(这里我就不详细说明配置过程了,不同的前端框架,配置过程不同,但框架从来不是技术实现的障碍,思路是一样的),
// 暗黑主题
[customize-theme-style='dark'] {
//背景
.alain-default {
background: @background-color;
}
//表格
.ant-table-thead>tr>th {
background-color: @table-head-color;
color: @font-color;
}
......
}
// 亮色主题
[customize-theme-style='light'] {
......
}
......
主题状态的改变会加载不同的css样式,接下来就是如何改变主题样式状态了
实现思路:我们将用户设置的主题状态用localStorage
存储在本地,如果用户没有设置,就加载默认主题。
//获取用户上次设置的主题
this.themeSkin = this.storageService.getStorageValue('customize-theme');
if (this.themeSkin) {
//设置主题
const body = document.getElementsByTagName('body')[0];
body.setAttribute('customize-theme-style', this.themeSkin);
}
//切换主题
changeSkin(skin) {
const body = document.getElementsByTagName('body')[0];
body.setAttribute('customize-theme-style', skin);
//存储主题
this.storageService.setStorageValue('customize-theme', skin);
}
完成以上步骤就基本上实现了自定义主题的切换需求。
然而事情并没有这么简单!!!
现在的框架都是提倡页面组件化,那么我们自己写的组件怎么适配主题呢?
所谓的适配也就是让组件读取当前载体文件body
节点(根节点)上的主题属性值,这个也不难做到。
举个栗子:
//自定义组件中的样式
:host-context([customize-theme-style='dark']) h4 {
.mixin-font-color('dark');
}
:host-context([customize-theme-style='light']) h4 {
.mixin-font-color('light');
}
//index.less
.mixin-font-color(@a) when(@a='dark') {
color: #ffffff;
}
.mixin-font-color(@a) when(@a='light') {
color: #212121;
}
编译后的css样式: [customize-theme-style='dark'][_nghost-fkw-c5] h4[_ngcontent-fkw-c5], [customize-theme-style='dark'] [_nghost-fkw-c5] h4[_ngcontent-fkw-c5] { color: #ffffff; }
:host-context()伪类选择器可以读取最外层的挂载点上的属性,通过这一特性就能实现组件主题化了 (API:angular.io/guide/compo…) 这里就给我们的组件中标题生成了一个唯一的样式。 至此,我们的主题需求问题就迎刃而解了。 如果有什么疑问或者你有更好的解决方案,欢迎留言交流,谢谢~