Vue组件库 | 主题替换、一键换肤

7,715 阅读5分钟

写在前面

相关链接

Varlet组件库 Github Star支持一下作者 Varlet组件库中文文档 Varlet组件库英文文档

大家好,作者又来了,熟悉作者的人都知道,作者是一个喜欢倒腾组件库的一个三好青年(吃的好,喝的好,睡的好)。国庆假期也结束了一周了,工作的节奏也回来了,组件库也要继续倒腾了。看到标题的小伙伴们可能也猜到了,作者不会平白无故的介绍什么主题替换和一键换肤(来抓壮丁了)。

Varlet实际上很早就支持主题替换和一键换肤,在下文中作者也会结合作者的视角和用户的视角介绍主题替换实现方式,希望对大家有帮助吧。Varlet接下来计划定义一套暗黑模式主题样式,虽然作者是三好青年,但是作者的肝,唉~不怎么好~(疯狂暗示), 而且人多力量大(掘友很棒!),有兴趣加入我们的朋友可以去我们的 issue列表 留言

核心逻辑

主题替换说白了就是将我们应用的css样式在运行时进行批量替换或是覆盖,并且做到对代码没有很强的侵入性,把主题和逻辑进行解耦,主流的方式有大概两种

构建时的样式变量的替换方案

设计者视角

由于我们现在的应用大部分都会使用构建工具(Vue Cli或是Vite),所以我们可以在构建时对样式进行编译处理,从而达到属性A -> 属性B的效果,这里以less作为例子。作者在书写组件样式时,对期望替换的样式都进行了变量的抽取,尤其是一些通用的样式,如颜色,字体...

image.png

以上只是一些通用的样式变量,不同的组件都有其自己的变量和属性...

用户视角

Vue Cli用户通过less loader修改变量值,比如修改主题颜色。

### Vue cli
```js
// vue.config.js
module.exports = {
  css: {
    loaderOptions: {
      less: {
        modifyVars: {
          'color-primary': '#009688',
        }
      }
    }
  }
}

Vite用户依然是通过简单的配置就能做到变量替换, 因为替换主题变量的能力是less预处理器提供的, 不同的构建工具只是对它进行了集成。 不过这里顺道提一句, Vite~的解析和Vue Cli不同, 因为~实际上是less-loader的语法,所以在Vite中要特殊处理一下这种语法,不过这里跟本文的主题内容关系不大。

// vite.config.js
import { defineConfig } from 'vite'

export default defineConfig({
  css: {
    preprocessorOptions: {
      less: {
        modifyVars: {
          'color-primary': '#009688',
        }
      }
    }
  },
  resolve: {
    alias: [
      { find: /^~/, replacement: '' }
    ],
  }
})

实现运行时一键换肤

虽然我们使用预处理器对变量进行替换已经能解决部分需求了,但是如果我们希望能在应用运行时进行主题替换应该怎么做呢?实际上作者不推荐使用构建时的替换方案去做运行时的一键换肤,如果一定要做,可以使用 webpack-theme-color-replacer 来做,它是通过拆分主题生成不同的css文件来做替换的,替换时会去请求对应的主题文件,伴随着网络请求,所以效率相对比较低。下文会介绍基于css var的方案,这也是作者十分推荐的一种方案。

基于css var的运行时样式变量替换方案

设计者视角

css vars是浏览器提供的特性,是原生运行时的样式变量。最大的优势就是运行时的替换速度十分之快,设计组件时在:root上挂载好样式变量,然后通过浏览器提供的api进行修改就完事了。

:root {
  --font-size-xs: 10px;
  --font-size-sm: 12px;
  --font-size-md: 14px;
  --font-size-lg: 16px;
  --icon-size-xs: 16px;
  --icon-size-sm: 18px;
  --icon-size-md: 20px;
  --icon-size-lg: 22px;
  --color-primary: #3a7afe;
  --color-info: #00afef;
  --color-success: #00c48f;
  --color-warning: #ff9f00;
  --color-danger: #f44336;
  --color-disabled: #e0e0e0;
  --color-text-disabled: #aaa;
  --cubic-bezier: cubic-bezier(0.25, 0.8, 0.5, 1);
}

用户视角

用户可以通过api或者直接通过css覆盖的形式对root上的css变量的进行替换,css覆盖就不过多解释了,如下是通过api的修改方式,还是十分方便的。

document.documentElement.style.setProperty('--color-primary', '#aaa')

Varlet的实现方式

我们是less预处理器 + css vars结合的方式,也就是”我都要,我都可以“,所以定义出来的组件样式会是这个样子...

image.png

这样用户既可以构建时替换,也可以css vars替换,全凭用户的喜好去选择适合的方式。在建立了主题替换机制之后伴随来的问题是因为样式变量过多,大部分用户可能没有成本和精力去对每个组件进行定制,但是又希望有一键换肤的能力,这就是我们组件库设计者应该做的事情了,我们需要为用户第一方的提供主题,比如体验相对优秀的暗黑模式主题。也就是Varlet下一步的计划。

写在最后

Varlet1.0发布之后的每一个特性,几乎都是组织掘金有兴趣的小伙伴一起来进行开发的,Varlet也是完全不盈利的,因为我们都很享受这种纯粹为了兴趣的开发节奏和交流氛围,也感受到了开源带来的乐趣,互相学习,互相吐槽,并且我们努力做好每一个特性,认真回复每一个issue。有兴趣参与贡献的小伙伴可以加入我们一起做事情, 我们十分期待。