效果展示
实现原理
通过写多套样式文件,在项目编译完成后动态更改link标签引入
实现过程
首先如果需要多套主题,必须要有多套样式文件,而样式文件又有很多写法,下面分享一下我的写法
主题css
首先我写了两套配色方案的css文件
这一步是关键,首先在 public 文件夹中创建一个theme文件夹用于存放主题配色css文件
之所以放在public文件夹中是防止webpack将其编辑打包了,后面我们引入需要用到
暗色和亮色配色方案
//dark.css
:root {
--main-color: #505690;
--bg-color-1: #282c34;
--bg-color-2: #21252b;
--bg-color-3: #4d5565;
--text-color-1: #adb3c0;
--animate-duration: 0.6s;
--shadow: 2px 2px 5px 0 rgba(31, 31, 31, 0.7);
}
//light.css
:root {
--main-color: #505690;
--bg-color-1: #f9f9f9;
--bg-color-2: #ffffff;
--bg-color-3: #ececec;
--text-color-1: rgb(50, 50, 50);
--animate-duration: 0.6s;
--shadow: 2px 2px 5px 0 rgba(31, 31, 31, 0.7);
}
全局样式
然后我写了一套直接在项目中引入的全局样式文件,在 main.js 中引入,下面放一部分示例代码,实际根据自己需求来
//global.scss
button,
div {
font-size: 14px;
color: var(--text-color-1);
outline: none !important;
}
img {
object-fit: cover !important;
}
/deep/.el-button {
border-radius: 0.5rem !important;
}
#nprogress .bar {
background: #505690 !important;
}
::-webkit-scrollbar {
display: none;
}
.text-main {
color: var(--text-main);
}
.bg-color-1 {
background-color: var(--bg-color-1);
color: #adb3c0;
}
.bg-color-2 {
background-color: var(--bg-color-2);
color: #adb3c0;
}
.bg-main {
background-color: var(--main-color);
color: white;
}
上面这套scss的要求就是你基本上全部颜色都要引用主题颜色里的颜色变量,已确保全局切换的时候能够覆盖到所有位置
页面文件
在实际的页面中样式的写法如下图(仅做示例,已删除相关业务代码)
// example.vue
<template>
<div>
<el-dialog
:append-to-body="true"
:visible.sync="show"
:destroy-on-close="true"
:close-on-click-modal="false"
:title="title"
width="30%"
>
<slot></slot>
</el-dialog>
</div>
</template>
<style lang='scss' scoped>
/deep/.el-dialog__body,
/deep/.el-dialog__header {
background: var(--bg-color-1);
}
/deep/.el-dialog__title {
color: var(--text-color-1);
}
</style>
动态引入
创建本地主题变量
首先你需要在localstroage里存储一个变量用于保存用户的主题类型,以便于用户进入网站时初始化样式,我用的是vuex+数据持久化实现的,所以我只需要在vuex中添加一个变量theme,在用户需要修改样式的时候触发changeTheme方法即可修改
示例代码
export default new Vuex.Store({
state: {
'theme': 'light'
},
mutations: {
changeTheme(state, theme) {
state.theme = theme
}
},
actions: {},
modules: { ...initModules() },
plugins: [
createPersistedState({
paths: ['theme'],
storage: window.localStorage,
}),
],
})
在App.vue中动态使用link引入主题样式
<!-- App.vue -->
<template>
<div>
<router-view></router-view>
</div>
</template>
<script>
export default {
watch: {
'$store.state.theme': {
handler() {
this.changeTheme()
},
immediate: true
}
},
components: {},
mounted() {},
methods: {
changeTheme() {
const theme = this.$store.state.theme
const head = document.head
// 遍历页面所有的link节点
const links = document.getElementsByTagName('link')
for (let i in links) {
// 如果已有引入主题样式则删除
if (links[i].href) {
if (
links[i].href.indexOf('dark.css') !== -1 ||
links[i].href.indexOf('light.css') !== -1
) {
head.removeChild(links[i])
}
}
}
// 创建新的主题节点插入head
var link = document.createElement('link')
link = Object.assign(link, {
href: 'theme/' + theme + '.css',
type: 'text/css',
rel: 'stylesheet'
})
head.appendChild(link)
}
}
}
</script>
<style lang='scss' scoped>
</style>