前言
在需求中会遇到一种业务场景,也就是后端下发颜色,前端再根据颜色进行渲染页面的主色调。还有一种场景是用户在前端自行更改主题色,最典型的就是白天/黑夜模式
那我们一般怎么实现呢?最简单的方法就是使用 JS 去动态修改 dom 节点的样式
我们这里用 vue 项目来演示这个代码,设计一个按钮,点击之后修改背景色。演示demo:
// template
<div @click="handleClick">改变颜色</div>
// script
function handleClick() {
let color = generateRandomColor();
// 修改颜色相关逻辑
}
function generateRandomColor() {
const letters = "0123456789ABCDEF";
let color = "#";
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
具体实现
JS 方案
实现
我们这边直接使用 JS 去修改背景色
let color = generateRandomColor();
// 修改颜色相关逻辑
document.body.style.backgroundColor = color
具体的效果如下:
缺点
如果是多个颜色需要变化,要写很多JS代码,代码会很复杂,而且不好维护
CSS变量方案
首先看下什么是 css 变量(自定义属性)。css 变量是由 CSS 作者定义的,它包含的值可以在整个文档中重复使用。通过自定义属性标记设定值(--main-color:black),再通过 var()函数来获取值(color:var(--main-color))
用法
element {
--main-color: red
}
文档推荐我们的最佳实践是定义在根伪类:root下,这样就可以在 HTML文档的任何地方进行访问了
:root{
--main-color: red
}
然后我们在某个 DOM 下就可以使用这个变量了
.text {
color: var(--main-color)
}
如何修改这个变量的值呢?使用 JS 即可
// 如果是 :root 下面
document.body.style.setProperty('--main-color','green')
// 如果是在某个元素下面
element.style.setProperty('--main-color','green')
如何在 JS 中获取这个值?
// 如果是 :root 下面
document.body.style.getProperty('--main-color','green')
// 如果是在某个元素下面
element.style.getProperty('--main-color','green')
实现
我们先在全局定义一个颜色变量
// style.css 中,就是在全局的 css 样式中
:root {
--bg-color: '#fff'
}
在要更改颜色的地方,使用变量var
body {
background-color: var(--bg-color);
}
在点击的时候通过 js 修改 --bg-color 的值即可
let color = generateRandomColor();
// 修改颜色相关逻辑
document.body.style.setProperty("--bg-color", color);
效果和上面也是一样的
这时候你可能会想,不也是通过 JS 去修改嘛?有什么区别?我直接 JS 一把梭不也好的很。其实还是有区别的,上面你使用 JS 是修改个别 DOM 的值,而这里是修改全局颜色的变量,你修改完全局颜色变量之后,所有用到这个变量的颜色都会改变。
如果你是使用 vue/react 响应式的话,也可以直接修改变量的值,然后通过响应式,去修改用到这个变量的地方,当然也可以。但是你有没有发现,你的数据修改层始终停留在 JS 上,再通过 JS 去修改 CSS层面,我们这个只能称为 JS 变量。
而我们使用 CSS 变量后,我们就从 JS 维度脱离,而是修改 CSS 维度让 DOM 进行了变化,所以这也是为什么称为 CSS 变量。
兼容性
兼容性已经比较好了
如果你要兼容 IE 或者低版本,可以添加
css-vars-ponyfill 来实现
Vue实现
vue3 中有一个CSS 中的 v-bind() ,更加语义化展示了 CSS 变量,我们可以在 JS 层直接下沉到 CSS 层维度去做 CSS 的变化。
实现
// templete
<div class="text-color"></div>
// script 声明一个变量
const bgcolor = ref("#fff");
function handleClick() {
let color = generateRandomColor();
bgcolor.value = color;
}
// style 中直接使用
.text-color {
width: 100px;
height: 100px;
background-color: v-bind(bgcolor);
}
实现的效果如下:
我们可以通过查看这个节点的颜色发现,使用的同样是 CSS 变量
原理
在组件内声明的变量会通过内联样式的方式添加在这个组件的 dom 节点上,并对变量名进行 hash 处理,然后会在 css 中通过 var 方式去引用这个变量。
局限性
由于 vue 是通过 app 挂载上去的,所以这个变量最多是声明在 #app 这个节点下面的,body 上就应用不上了。
总结
在我们遇到一些比较复杂的需要全局配置的换肤业务场景的时候,不妨试一下 CSS 变量,会让你更加容易维护你的代码,更加优雅