配置打包命令动态应用主题样式

假设我们的代码里提供了蓝色、黑色和红色等多个主题风格样式,并且一个项目上只需要使用一套主题风格:
在这里插入图片描述
假如某个项目需要使用蓝色主题,那么我们可能会在main.js中这样从theme-blue下引入对应的css文件:

...
import '../assets/theme-blue/index.css';
...

然后我们执行打包命令,蓝色主题下的样式文件就会被引入,于是项目就应用了蓝色主题。

假如现在另一个项目需要生成一套红色主题该怎么办呢?

非常简单,把上面的代码改成从红色主题下引入文件即可:

import '../assets/theme-red/index.css';

这样的做法我们可能已经司空见惯了。但是为了应用不同主题而直接修改源代码是非常不好的方式,它会导致代码的稳定性急剧下降。

我们有更好的做法:配置打包命令。下面我们来介绍如何仅通过配置打包命令来生成多套主题样式(关于打包命令的基本说明请参考我之前的博客:多页vue应用的单页面打包方法(内含打包模式的应用))。

1. 新增打包命令

我们首先打开package.json,向script字段添加两个新的命令,分别是:build-bluebuild-red,我们将会分别用这两个命令打包蓝色主题和红色主题。命令如下:

{
  ...
  "scripts": {
    ...
    "build-blue": "vue-cli-service build --mode themeBlue",
    "build-red": "vue-cli-service build --mode themeRed"
  }
}

前文我们说过,--mode后面的参数指定的是打包模式,这里我们分别为两个命令应用themeBluethemeRed这两个打包模式。

那么这两个模式到底是什么呢?我们还没有定义。接下来我们就去定义这两个模式。

2. 配置env文件

前文我们也谈到过,启用一个打包模式的本质就是启用一组变量。这组变量会被写入process.env对象(process是webpack打包的进程对象)内,供webpack打包时读取。

每个打包模式使用的变量必须定义在项目根目录下,命名为.env.xxx(其中xxx就是对应的打包模式)。因此我们在项目根目录下新建以下两个文件:

project-demo
  |-- src
  |-- ...
  .env.themeBlue
  .env.themeRed

注意,这两个文件均以.开头,没有后缀,.env.后面的部分是对应的打包模式。

当执行npm run build-blue时,启用的是.env.themeBlue中定义的变量,build-red同理。

我们现在编辑这两个文件,设置一个变量:
.env.themeBlue

theme='theme-blue'

.env.themeRed

theme='theme-red'

现在当执行npm run build-blue时,webpack就会读取.env.themeBlue,然后把process.env.theme的值设置为'theme-blue'

注意:如果要在src中使用这个变量,它必须以VUE_APP_开头(如VUE_APP_THEME),否则访问不到,src之外没有这个限制(如vue.config.js中)。

3. 配置路径别名

如果是在js中使用打包命令,上面两步就足够了,这个我们在前文已经讲过,不再赘述。

但是这样对于样式文件是行不通的,因为样式文件一般是用import导入的,它会在静态分析阶段就导入进来,因此无法在路径中写入js变量。也就是说下面的写法是无效的:

import '../assets/' + process.env.XXX + '.css';

既然不能在css路径里使用变量,那我们怎么根据打包命令引入不同的主题样式呢?

方法就是配置路径别名

我们修改vue.config.js文件,配置一个别名:

const path = require('path');
function resolve(dir) {
    return path.join(__dirname, dir);
}

let themeName = process.env.theme;
module.exports = {
  ...
  chainWebpack: (config) => {
    config.resolve.alias.
      set('&', resolve('src/assets/' + themeName));
  }
}

现在根据打包模式的不同,&可以表示不同的路径。当执行npm run build-blue时,&指代的路径是:src/assets/theme-blue;而执行npm run build-red时,它指代的是src/assets/theme-red

于是我们可以使用&作为主题样式路径的别名,像这样引入样式文件:

import '&/index.css';

现在当执行npm run build-blue时,引入的样式是来自于theme-blue文件夹;而执行npm run build-red时,引入的样式来自于theme-red。仅仅依靠执行不同的打包命令,我们就可以打包出不同主题的项目,再也不用为了切换主题修改代码了!

注意事项

(1). 不要在module.exports中读取process

经过测试,下面的代码运行无效:

chainWebpack: (config) => {
  config.resolve.alias
    .set('&', resolve('src/assets/css/'
      + process.env.theme));
}

webpack打包时并没有正确解析出process.env.theme的值,因此请将process.env.theme的值保存在module.exports外部的变量里,然后直接引入该变量。

(2). 使用@import引入样式

当在css中使用@import引入主题样式时,不能像import语句一样书写。

因为原生的css在使用@import语句时是运行时加载的,也就是说它不会在打包阶段被解析,所以下面的代码会被原样输出到打包结果中:

<style>
  @import '&/index.css';
</style>

这样样式文件必然会加载失败。那么怎么办呢?

一般我们的项目中都会引入一个loader:postcss来兼容样式问题,这时我们可以通过在路径开头添加一个~,告诉postcss这是一个需要在打包阶段编译的依赖:

<style>
  @import '~&/index.css';
</style>

这样,webpack在解析时,就会立即执行&/index.css这个资源,添加到依赖关系中,对应的样式文件就被正确加载了。

注意,这里的~只是一个标志符,配置别名时不需要带,postcss在解析路径时会自动去掉它。另外,测试发现,当vue.config.js中配置的别名以~$开头时,路径无法被正确解析(其他特殊字符未完全测试,是否合法以实际效果为准),因此请避免在别名中以这两个字符开头。

猜你喜欢

转载自blog.csdn.net/qq_41694291/article/details/107699542
今日推荐