一次换肤实战

语言: CN / TW / HK

背景

最近做的这个项目中,有个需求就是提供一个管理员平台,可以保存配置系统的主题色,然后每次加载的时候接口读取配置。好家伙,这我之前还真没做过,同时也觉得挺有意思的,就开始去寻找各位大佬现成的方案了。上百度,掘金上搜索起来。

方案

经过我的不懈百度大概总结出来以下两种方案

替换css中主题色,写入style

由于我项目本身用了element组件,想到换肤的时候,第一时间想到去看看element换肤的实现方式。 大概分为以下几步:

  • element换肤方式是保存默认主题色element-variables.scss文件,请求获取该主题色文件静态资源
  • 读取该css文件并去替换css文本里面匹配换肤需要替换的颜色
  • 将替换过后的css文本,写入到 style 上,再挂载到 document

具体代码可以参考element

最后我也是采用了这种方法

利用css3 var

css3 var 可以定义css参数,并且通过 document.body.style.setProperty 动态改变

scss文件代码

  /* var(变量名, default color) */
  $app-theme-color: var(--theme-color, #C93C2A);

复制代码

  const theme-color = 'green' // 需要替换的主题色
  document.body.style.setProperty('--theme-color', theme-color)

复制代码

这种方法我没有采用,因为需求是中途来的,所以主题色没定义哈哈哈(也是懒得改了)

总结

以上两种方法我也分析了一下优劣

动态替换css中主题色,写入style

优点:

  • 不考虑兼容性

缺点:

  • 动态替换主题色的时候需要请求默认主题色文件,刷新浏览器的时候能够看到颜色替换过程,体验不怎么好

css3 var

优点:

  • 修改之后渲染很快,刷新浏览器肉眼看不见替换颜色过程

缺点:

  • 兼容性,不兼容ie,具体可以去 MND 看

  • 无法在媒体查询中定义变量

  • 不能继承

最终实现

好了, 分析完优缺点了,最后来说说我是怎么用第一种方式实现的吧。

当我决定用第一种方式实现的时候就必须要解决一个问题,那就是我的主题色css文件从哪儿来。由于最后的css文件都是打包出来的,所以应该从打包着手。这个时候我们就需要一个webpack插件去帮我完成这件事,本来是打算自己撸这个插件的,为了节约时间(偷懒)用了个现成的 webpack-theme-color-replacer 不过替换颜色过程是我自己写的

不过后面也看了 webpack-theme-color-replacer 原理,在 webpack 打包的时候 compiler.emit 钩子生成资源到 output 目录之前。把含有主题色css的类打包到我们传入的 fileName

vue.config.js 配置部分代码:

  const ThemeColorReplacer = require('webpack-theme-color-replacer')

  new ThemeColorReplacer({
    matchColors: ['#C93C2A'], // colors array for extracting css file, support rgb and hsl.
    fileName: 'css/theme-colors-[contenthash:8].css', //optional. css 文件.
  })
  
复制代码

替换css文本插入style 部分代码参考


  // 创建style
  const elStyle = document.querySelector('head').appendChild(document.createElement('style'))
  elStyle.setAttribute('id', 'theme-style')
  // 插入主题css替换颜色后文本
  // updateStyle 正则替换匹配颜色,将默认换成需要替换的颜色val
  // getCSSStringOn ajax 获取css主题文本内容
  // window.__theme_COLOR_cfg.url 是项目主题文件路径
  // oldVal 默认主题色
  // val 替换颜色
  elStyle.innerText = this.updateStyle(await this.getCSSStringOn(window.__theme_COLOR_cfg.url), [oldVal], [val])

复制代码

可能参考代码不多, 但是流程说清楚就行了

关于思考

虽然做出来了,但是每次接口请求 -> 替换 -> 渲染 这个过程会肉眼可见颜色替换过程,实在是难受,而且咱也不可能阻塞渲染等让用户等着我们替换完吧。优化这个我义不容辞,等我想出来哈哈

tips: 作者憨憨新手一个,写的不好见谅见谅哈哈

分享到: