如何巧妙的实现系统的主题切换

前言

这是我参与新手入门的第1篇文章

前几天不是一百周年呢,看了看一百周年庆典,人的思绪回到了那篇被鲜血染红的年代,激情瞬间澎湃。然后,打开很多软件都是鲜红的一片,我又想起了之前接触的系统,想了想,还是想对之前主题的实现思路做个总结。

方案选择

  • 在body节点上动态修改class名,根据不同的class名,写入不同的样式。每次切换,更改body上的类名,达到换肤效果
  //类似于这样

  body.red{
    box{
      color:red;
    }
  }

  body.blue{
    box{
      color:blue;
    }
  }
复制代码
  • 每种主题各写一个css文件,通过动态修改引入样式文件达到换肤。

这里,我想到如果是十几个主题,我如果选择第一种来写,极有可能就会复制漏掉几个,或者说内心就是极度抗住这种实现方式的。毫无疑问,我肯定选择第二种。毕竟样式文件的区分是极利于维护的。这里我也主要想说第二种实现方式,第一种通过度娘可以很快的度出来,当然也许大家有更好的实现方式,也欢迎大家留言,我去学习学习。

实现步骤

方案定了,那么肯定想着得怎么实现了。一个问题的解决总是得伴着几步走的。我的步骤就很简单了

  • 准备一个下拉框,带有主题色和炫酷的主题名
  • 每个主题准备一个样式文件
  • 切换主题时读取对应的样式文件使其生效
  • 后续优化问题,这里做到这里再考虑

1.准备一个可选择主题的样式

这个很简单,我选择的的是一个下拉框的形式,大家也可以选择排列的形式来展现。

主题下拉框.png

2.每个主题一个单独的样式

这个也很简单,对每个主题创建一个对应的文件名样式文件即可,我的样式目录如下

样式目录结构.png

这里我是用scss中的变量来实现一个样式文件中的所有主题色替换的,,至于为什么启用 .useable 作为样式文件后缀,我们后面说。顺便这里要将 ‘悲伤灰’的颜色单独说下。这里复制了网上的一段通用代码,进行整体的置灰,而不需要一个个样式的写。

 html {
   -webkit-filter: grayscale(100%);
   -moz-filter: grayscale(100%);
   -ms-filter: grayscale(100%);
   -o-filter: grayscale(100%);
   filter: grayscale(100%);
   filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1);
 }
复制代码

这点也是最重要的一点,也是主题实现的核心 。首先我们得遍历所有的主题文件,然后每次下拉切换主题时,获取到对应的那一个文件并且引用。首先想到的就是用 VUEX状态管理将每次切换的状态存储下来,然后计算属性获取,每次切换,调用一个方法去获取当前的主题样式并引用,感觉这样的思路也能实现,但个人觉得还不够完美。在一番搜索之后,我找到了 style-loaderuseable 中 的 lazyStyleTag,并配合 css.use()css.unuse() 达到了样式文件的完美引入与删除。这里 .useable作为文件后缀,也是style-loader的api推荐的 ,具体代码如下:

vue.config.js中

//全局配置scss文件的方法
  const scss = config.module.rule("scss").toConfig();
  //用.useable.scss是style-loader的api对应有 use 和 unuse 方法,动态加载删除link文件
  const useable = {
    ...scss.oneOf[3],
    test: /\.useable.scss$/
  };
  useable.use = [...useable.use];
  useable.use[0] = {
    loader: "style-loader",
    options: {
      injectType: "lazyStyleTag"
    }
  };
复制代码

其中 lazyStyleTag是用来延迟加载css文件的,我也是从这里lazyStyleTag才更好的理解此属性。

这里是我准备的主题json,文件名是 theme.js

const themes = [
  {
    value: "#000",
    text: "经典",
    name: "chalk",
  },
  {
    value: "white",
    text: "极光白",
    name: "white"
  },
  {
    value: "#266EB3",
    text: "科技蓝",
    name: "blue"
  },
  {
    value: "#AF0009",
    text: "中国红",
    name: "red"
  },
  {
    value: "#058F3D",
    text: "护眼绿",
    name: "green"
  },
  {
    value: "#DBB579",
    text: "高贵金",
    name: "golden"
  },
  {
    value: "gray",
    text: "悲伤灰",
    name: "gray"
  },
  {
    value: "#DE0716",
    text: "醒目红",
    name: "lightred"
  }
];
export default themes;
复制代码

所有的东西都准备好了,现在就只剩动态获取css文件了。这里,我的具体实现是这样的 style/theme/index.js

import themelist from "@/layout/components/theme";
const cache = {};
let themeAction = {};
themelist.forEach(theme => {
  const item = theme.name || theme;
  themeAction[item] = function() {
    if (!cache[item]) {
      cache[item] = require("./" + item + ".useable.scss").default;//.useable是style-loader中api,动态加载删除link文件
    }
    return cache[item];
  };
});
let current = null;
export async function setTheme(theme){
  let style = await themeAction[theme]();
  //style-loader的api
  if(current){
    current.unuse();
    //或style.unref();
  }
  style.use && style.use();
  //或style.use && style.ref();
  current = style
}
复制代码

这里需要注意的一点思想就是,每次切换的时候,我都会将本次的主题存起来,下次切换之前,删掉再引用,这样就不会一直累加引入的样式文件导致冲突啦,而且也算是一点小优化。

这里因为电脑原因,暂时没法贴上gif图了,就动态贴上几张截图展示下效果,忘各位见谅。

主题红.png

主题灰色.png

主题绿色.png

然后,非常感谢掘金的此次活动,让我迈开了动手写文章的第一步,也许此文有太多漏洞和组词不合适的地方,还望各位大佬见谅,谢谢。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享