背景
很多网站都有一键换肤的功能非常炫酷,多主题切换针对不同的系统也很方便,本文来探讨下基于 vue 的方案
开胃菜--暗黑模式
自从 macOS的暗黑模式 Dark Mode 出现以后就火起来了,具体效果如下
核心依赖就是 prefers-color-scheme 实践代码如下
@media (prefers-color-scheme: dark) {
body { background: #333; color: white; }
}
/* 浅色模式 */
@media (prefers-color-scheme: light) {
body { background: white; color: #333; }
}
css 级联变量 variables
mdn 上的自定义属性(有时候也被称作CSS变量或者级联变量)是由CSS作者定义的。声明一个自定义属性,属性名需要以两个减号(--)开始,属性值则可以是任何有效的CSS值。
<template>
<div>
<header id="header">
<h1>我换肤了啊😄</h1>
</header>
<button id="btn-red">red</button>
<button id="btn-black">black</button>
<button id="btn-blue">blue</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
mounted () {
const btns = document.querySelectorAll("button");
const btnArray = Array.from(btns);
btnArray.forEach( node => {
node.addEventListener("click",function(e){
var ele = e.target||e.srcElement;
var styles = getComputedStyle(ele);
var value = styles.getPropertyValue("--"+ele.id);
document.documentElement.style.setProperty("--theme-color",value);
})
})
},
created () {
}
}
</script>
css 部分
:root {
--theme-color: #989898;
}
#header {
width: 100%;
height: 50px;
line-height: 50px;
background-color: var(--theme-color);
margin-bottom: 30px;
}
#header h1 {
color: #fff;
}
button {
width: 100px;
height: 30px;
color: #fff;
border: none;
}
#btn-red {
--btn-red: #ff9a9e;
background-color: var(--btn-red);
}
#btn-black {
--btn-black: #a1c4fd;
background-color: var(--btn-black);
}
#btn-blue {
--btn-blue: #a18cd1;
background-color: var(--btn-blue);
}
效果
核心就是去设置 document.documentElement.style.setProperty("--theme-color",value);
css in js
对于大多数 vue 的开发人员来说不是很友好,但也不失位一种思路,核心依赖是 styled-components 的语法 具体代码如下
import styled from 'vue-styled-components';
const btnProps = { primary: Boolean };
const StyledButton = styled('button', btnProps)`
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
background: ${props => props.primary ? '#a1c4fd' : 'white'};
color: ${props => props.primary ? 'white' : '#a1c4fd'};
`;
export default StyledButton;
<s-button>我是一个普通的按钮</s-button>
<s-button primary>我是一个主题色按钮</s-button>
效果就不放了,可以看出有点类 jsx 写法。 对于 react 用户挺友好 配合 styled-components提供ThemeProvider 来切换主题,效果非常好
link alternate (重点)
方法借助HTML rel属性的alternate属性值实现
<link href="reset.css" rel="stylesheet" type="text/css">
<link href="default.css" rel="stylesheet" type="text/css" title="默认">
<link href="red.css" rel="alternate stylesheet" type="text/css" title="红色">
<link href="blue.css" rel="alternate stylesheet" type="text/css" title="蓝色"
通过上面我们可以看出有 title 和 无 title 以及 alternate 的区别
| 无 title | 有 title | alternate |
|---|---|---|
| 无论如何都会加载并渲染 | 默认样式CSS文件加载并渲染 | 备选样式CSS文件加载,默认不渲染 |
| reset.css | default.css | red.css、blue.css |
接下来放下实战
<template>
<div>
<header id="header">
<h1>我换肤了啊😄</h1>
</header>
<button id="btn-red" @click="change('红色')">red</button>
<button id="btn-blue" @click="change('蓝色')">blue</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
methods: {
change(data) {
console.log(data, "data")
var eleLinks = document.querySelectorAll('link[title]');
[].slice.call(eleLinks).forEach(function (link) {
link.disabled = true;
if (data === link.title) {
link.disabled = false;
}
});
}
}
}
</script>
看到这是不是感觉有种熟悉的感觉,有点类似动态切换 **link href **的感觉,使用JS改变href属性会带来加载延迟,样式切换不流畅,体验不佳。使用这个有下面两种好处
- 兼容性好, Chrome、Firefox、ie8 均支持
- 体验更好,瞬间切换平滑过过渡。我们已经先加载了 css 不需要再发请求,缺点就是多份主题在没有上 http2 的网站上可能效果不是最佳,毕竟队头阻塞。建议配合 http 使用更佳
element-ui
既然用了 vue 就不得不提一下使用最多的 ele ui 库,首先来看看他们的 官方效果 思路如下
他们的方案很明显,命名什么都是有规律可言,需要提前定好规则
总结
其实还有很多其他方案,比如基于 sass 、less 变量这种,很多文章讲的都很通透,这边我就不重复讲了。总体而言我个人还是更喜欢 link alternate 模式对于老工程侵入性不是特别强,对于新的工程规范还是挺重要还是 ele 那套方案比较好用
彩蛋
安利一个渐变色的网站传送门