electron-vue项目笔记7——实现主题换肤

3,911 阅读4分钟

不推荐直接使用less或css变量方式来换肤,因为有些定制不仅仅是某些样式颜色不一样,当然,如果你的定制仅仅更换相同地方的颜色,那你使用less/scss变量会更节省资源

1. 创建相应的换肤脚本index.js

  1. 新建一个主题文件夹theme并创建相应的换肤脚本index.js以及主题样式文件theme.scsstheme1.scss

其中theme.scsstheme1.scss是两个主题,你可以根据需要创建

2. 换肤脚本index.js

刷新之后会,主题会丢失,如果想要F5刷新之后不丢失主题,可以直接查看第6步优化方案的完整实现

  1. 换肤脚本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引入

  1. 在渲染进程中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中主题选项

  1. 在vue组件中使用this.$themeSetup(key名) 其中key名就是themeOptions中主题选项

例如:

this.$themeSetup("theme_0")

5. 在vue组件外使用themeSetup(key名)

  1. 在vue组件外使用themeSetup(key名) 因为themeSetup方法已经暴露给windows了,所以可以直接取值

6. 刷新之后会,主题丢失的优化

利用localstorage来缓存,然后再初始化的时候加载判断,如果缓存过就直接设置,如果没有缓存过,就设置默认主题

  1. 编写完整的主题逻辑脚本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为文件名,请这样子设置