不推荐直接使用less或css变量方式来换肤,因为有些定制不仅仅是某些样式颜色不一样,当然,如果你的定制仅仅更换相同地方的颜色,那你使用less/scss变量会更节省资源
1. 创建相应的换肤脚本index.js
- 新建一个主题文件夹
theme
并创建相应的换肤脚本index.js
以及主题样式文件theme.scss
、theme1.scss
其中theme.scss
、theme1.scss
是两个主题,你可以根据需要创建
2. 换肤脚本index.js
刷新之后会,主题会丢失,如果想要F5刷新之后不丢失主题,可以直接查看第6步优化方案的完整实现
- 换肤脚本
index.js
import Vue from 'vue'
const DEFAULT_THEME = 'theme_0' // 默认主题
let themeOptions = {
"theme_0":'./theme.scss',
"theme_1":'./theme1.scss'
}
export const theme = {
currentTheme: DEFAULT_THEME,// 当前主题
themeOption: themeOptions,//当前主题可选项
}
/*------------------------------------
* 设置主题
对外提供一个setup方法,用来修改我们的主题;
由于需要兼容多主题样式,所以这里在body上加入了相应的类名,方便我们做样式定制
------------------------------------*/
export const themeSetup = themeName => {
if(!themekey){
return
}
let themeName = themekey.replace(/\s+/g,"");//去除所有空格
// 如果已经添加过这个样式,就不再重复添加
if(document.body.classList){
if(Array.from(document.body.classList).includes('theme-' + themeName)){
console.log("你已使用该主题")
return
}
}
// 如果设置没有在可选主题里,则不可修改主题
if(!themeOptions[themeName]){
console.log("没有在可选主题里,不可修改主题")
return
}
// 如果有主题dom,移除
let themeDom = document.querySelectorAll("style[theme]");
if(themeDom){
for(let i = 0 ; i < themeDom.length ; i++){
document.head.removeChild(themeDom[i])
}
}
// 给body创建一个主题标记
Object.keys(themeOptions).forEach(themeName => {
document.body.classList.remove(`theme-${themeName}`)
})
document.body.classList.add(`theme-${themeName}`)
document.body.setAttribute('theme', themeName)
theme.currentTheme = themeName;
require (`${themeOptions[themeName]}`);//引入
document.head.lastChild.setAttribute('theme', themeName);//给新添加的style设置theme属性
console.log("修改主题成功")
}
//把切换主题绑定到window上,方便在vue组件以外的文件使用语言包
window.theme = theme
window.themeSetup = themeSetup
. 如果你懒得每一个主题都写入文件里,那么你可以使用file来自动导入生成主题可选,以后写的每一个主题都不需要手动加入主题选项里,比较推荐的写法是:
const DEFAULT_THEME = 0 // 默认主题
let themeOptions = {};//可选主题
/*------------------------------------
批量自动导入可选主题
------------------------------------*/
const files = require.context('.', false, /\.scss$/)
files.keys().forEach((key,index) => {
if (key === './index.js') return
themeOptions[index]=key;
})
export const theme = {
currentTheme: DEFAULT_THEME,// 当前主题
themeOption: themeOptions,//当前主题可选项
}
/*------------------------------------
* 设置主题
对外提供一个setup方法,用来修改我们的主题;
由于需要兼容多主题样式,所以这里在body上加入了相应的类名,方便我们做样式定制
------------------------------------*/
export const themeSetup = themekey => {
if(!themekey){
return
}
let themeName = themekey.replace(/\s+/g,"");//去除所有空格
// 如果已经添加过这个样式,就不再重复添加
if(document.body.classList){
if(Array.from(document.body.classList).includes('theme-' + themeName)){
console.log("你已使用该主题")
return
}
}
// 如果设置没有在可选主题里,则不可修改主题
if(!themeOptions[themeName]){
console.log("没有在可选主题里,不可修改主题")
return
}
// 如果有主题dom,移除
let themeDom = document.querySelectorAll("style[theme]");
if(themeDom){
for(let i = 0 ; i < themeDom.length ; i++){
document.head.removeChild(themeDom[i])
}
}
// 给body创建一个主题标记
Object.keys(themeOptions).forEach(themeName => {
document.body.classList.remove(`theme-${themeName}`)
})
document.body.classList.add(`theme-${themeName}`)
document.body.setAttribute('theme', themeName)
theme.currentTheme = themeName;
require (`${themeOptions[themeName]}`);//引入
document.head.lastChild.setAttribute('theme', themeName);//给新添加的style设置theme属性
console.log("修改主题成功")
}
//把切换主题绑定到window上,方便在vue组件以外的文件使用语言包
window.theme = theme
window.themeSetup = themeSetup
3. 在渲染进程中main.js引入
- 在渲染进程中main.js引入
/**------------------------------
主题
--------------------------------*/
import {theme,themeSetup} from './assets/css/theme/index.js'
Vue.themeSetup = Vue.prototype.$themeSetup = themeSetup
Vue.theme = Vue.prototype.$theme = theme
4. 在vue组件中使用this.$themeSetup(key名)
其中key名就是themeOptions中主题选项
- 在vue组件中使用
this.$themeSetup(key名)
其中key名就是themeOptions中主题选项
例如:
this.$themeSetup("theme_0")
5. 在vue组件外使用themeSetup(key名)
- 在vue组件外使用
themeSetup(key名)
因为themeSetup方法已经暴露给windows了,所以可以直接取值
6. 刷新之后会,主题丢失的优化
利用localstorage来缓存,然后再初始化的时候加载判断,如果缓存过就直接设置,如果没有缓存过,就设置默认主题
- 编写完整的主题逻辑脚本
index.js
const DEFAULT_THEME = 'theme' // 默认主题
let themeOptions = {};//可选主题
/*------------------------------------
批量自动导入可选主题
------------------------------------*/
const files = require.context('.', false, /\.scss$/);//第一个参数表示相对的文件目录,第二个参数表示是否包括子目录中的文件,第三个参数表示引入的文件匹配的正则表达式。
files.keys().forEach((key) => {
if (key === './index.js') return
let optionKey = key.substring(key.lastIndexOf('/')+1,key.lastIndexOf('.scss'));//截取文件名称
themeOptions[`${optionKey}`]=key;//设置key为文件名
})
/*------------------------------------
* 设置主题配置
------------------------------------*/
export const theme = {
currentTheme: DEFAULT_THEME,// 当前主题
themeOption: themeOptions,//当前主题可选项
}
/*------------------------------------
* 设置主题
对外提供一个setup方法,用来修改我们的主题;
由于需要兼容多主题样式,所以这里在body上加入了相应的类名,方便我们做样式定制
------------------------------------*/
export const themeSetup = themekey => {
if(!themekey){
return
}
let themeName = themekey.replace(/\s+/g,"");//去除所有空格
// 如果以及添加过这个样式,就不再重复添加
if(document.body.classList){
if(Array.from(document.body.classList).includes('theme-' + themeName)){
console.log("你已使用该主题")
return
}
}
// 如果设置没有在可选主题里,则不可修改主题
if(!themeOptions[themeName]){
console.log("没有在可选主题里,不可修改主题")
return
}
// 如果有主题dom,移除
let themeDom = document.querySelectorAll("style[theme]");
if(themeDom){
for(let i = 0 ; i < themeDom.length ; i++){
document.head.removeChild(themeDom[i])
}
}
// 给body创建一个主题标记,方便定制
Object.keys(themeOptions).forEach(themeName => {
document.body.classList.remove(`theme-${themeName}`)
})
document.body.classList.add(`theme-${themeName}`)
document.body.setAttribute('theme', themeName)
theme.currentTheme = themeName;
require (`${themeOptions[themeName]}`);//引入
document.head.lastChild.setAttribute('theme', themeName);//给新添加的style设置theme属性
window.localStorage.setItem('THEME', themeName);//缓存主题
console.log("修改主题成功")
}
/**
* 主题初始化
* @function initTheme
* 从本地存储取,设置主题
*/
export const initTheme = function(){
let localTheme = window.localStorage.getItem('THEME')
if(localTheme){
themeSetup(localTheme);//设置本地缓存主题
}else{
themeSetup(DEFAULT_THEME);//设置默认主题
}
}
//把切换主题绑定到window上,方便在vue组件以外的文件使用语言包
window.theme = theme
window.themeSetup = themeSetup
window.initTheme = initTheme
你也可以在app.vue文件里,vue生命周期created来初始化
注意事项
webpack require context可以动态引入文件,可以去官网看看
require.context('.', false, /.scss$/);
- 第一个参数表示相对的文件目录,
- 第二个参数表示是否包括子目录中的文件,
- 第三个参数表示引入的文件匹配的正则表达式。
如果选择自动加载主题样式,请把自动加载的条件改一下
如果你想要主题选项的key为文件名,请这样子设置