vue-cli 初始化的项目动态换肤实践

原创
前端路迹
2021-2-26 16:58
编辑于 2022-6-17 17:03

换肤最终达到的目标如下:

  1. 只需要简单的配置,即可开发一套新主题
  2. 内置多套皮肤
  3. 可以随意设置颜色,生成新主题 CSS

使用 vue-cli 初始化一个项目 vue-switch-theme。

这里选的 CSS 预处理语言为 less。

一般实现多套主题都是预先定义好各种主题相关的变量,譬如主题色,文字颜色,导航背景色等等,不同的主题调整变量即可。为了可以迅速开发一套新的主题,那么理想情况下配置的变量越少越好。于是就有了以下主题开发的流程。

主题目录结构如下:

theme/blue/style.less 蓝色主题入口样式
theme/dark/style.less 暗黑主题入口样式
theme/default/style.less 默认主题入口样式
theme/template/style.less 模板主题,用于实时配置新的主题
theme/style.less 汇总系统所有样式
theme/var.less 全局主题 less 变量

theme/style.less 一般用来汇总项目中所有的样式。例如:

@import '../css/components/select-city.less';
@import '../css/components/calendar.less';
@import '../css/views/home.less';
@import '../css/views/about.less';

全局主题 less 变量文件包含了默认主题的变量,当然还包括和主题无关的一些变量。例如:

@calendar-bg-color: #f1f1f1;

@main-font-color: #444;
@tip-font-color: #999;

@nav-width: 200px;

每个主题 less 文件内容结构如下,里面的顺序不能随意更改:

// 引入全局主题变量
@import '../var.less';

// 主题相关配置
// 覆盖主题的变量配置
@primary-color: #008891;

// 引入系统样式合集
@import '../style.less';

这里要重点讲下 template 下的 style.less 文件如何去做, 这个 template 顾名思义是一个模板主题,最终会生成一个模板主题 CSS 文件,用户随意设置颜色,通过字符串的方式生成一个新的主题 CSS 文件。

template/style.less 要将所有主题相关的变量定义为一个占位符,这样可以方便做字符串替换。例如:

// 引入全局主题变量
@import '../var.less';

// 覆盖主题的变量配置
@primary-color: '__primary-color__';

@main-font-color: '__main-font-color__';
@tip-font-color: '__tip-font-color__';

// 引入系统样式合集
@import '../style.less';

主题开发相关的介绍完了。接下来介绍如何做打包的配置,从上面的主题开发的目录结构可以看到,每个主题最终都会生成一个 CSS 文件,这里有多种方式:

  1. 样式单独去打包,和项目(也就是 vue-cli-service serve/build)的打包分开。比如用 lessc 去打包,但是 less 中用到的图片,字体等资源就需要自己处理
  2. 跟着项目一起打包,但是 Webpack 也不支持直接配置类似多种主题的方案,这里我们就只能通过配置多入口项目的方式来解决这个问题。

在 vue.config.js 中配置各个主题入口。

module.exports = {
    filenameHashing: false,
    chainWebpack: (config) => {
        // 主题入口不需要生成 html
        config.plugins.store.delete('html-theme_blue')
        config.plugins.store.delete('html-theme_dark')
        config.plugins.store.delete('html-theme_template')
    },
    pages: {
        index: {
            entry: 'src/main.js',
        },
        theme_blue: {
            entry: 'src/assets/theme/blue/style.less',
        },
        theme_dark: {
            entry: 'src/assets/theme/dark/style.less',
        },
        theme_template: {
            entry: 'src/assets/theme/template/style.less',
        },
    },
}

默认主题在 main.js 中引入了,因此不需要再去单独配置入口。最终打包后的文件如下图所示。

打开 theme_template.css,主题相关的变量已经替换为占位符,当然替换的时候需要特别注意,不仅要替换占位符,也要将引号一并给替换了。

切换皮肤,其实就是加载相应皮肤的 CSS 文件。

比如在 php 后端通过 url 参数的方式加载不同的皮肤。

<?php if(\Yii::$app->request->get('theme') =='dark'){?>
<link rel="stylesheet" href="/css/theme_dark.css">
<?php }else{?>
<link rel="stylesheet" href="/css/index.css">
<?php }?>

或者直接用 JavaScript 加载不同的皮肤。

const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = '/css/theme_dark.css'
document.head.appendChild(link)

上面主要介绍了如何去加载内置皮肤,那么设置主题相关的变量,生成下新的主题 CSS 文件如何做?

最简单的方式:

  1. 将主题相关的变量提炼出来,可以做成配置页面,让用户配置
  2. 配置完成后,提交给后端生成 CSS 文件

URL参数的方式:

  1. 将主题配置 JSON 转成 base64,放到 URL 参数中,譬如 theme=xxx
  2. 后端将 base64 生成 md5 值,解析成主题配置,生成新的 CSS 文件
  3. 文件名用第二步生成的 md5 值,这样的好处是相同主题配置不用重复生成,实际生产中,还要考虑模板主题 CSS 文件的更新时间,如果生产更新是删除后更新没问题。

前面为了保障内置主题文件名固定,因此将 filenameHashing 禁用了。那么实际生产中这样做肯定不合适,因此我们需要将 hash 改到 url 参数中去。在 vue.config.js 中调整 filename 和 chunkfileName。

chainWebpack: (config) => {
    if (process.env.NODE_ENV === 'production') {
        config.output.filename('js/[name].js?[contenthash:8]')
        config.output.chunkFilename('js/[name].js?[contenthash:8]')
        config.plugin('extract-css').tap((args) => {
            args[0].filename = 'css/[name].css?[contenthash:8]'
            args[0].chunkFilename = 'css/[name].css?[contenthash:8]'
            return args
        })
    }
}
转载请注明出处。本文地址: https://www.qinshenxue.com/article/vue-cli-project-dynamic-theme-solution.html
关注 & 咨询问题