假设我们的代码里提供了蓝色、黑色和红色等多个主题风格样式,并且一个项目上只需要使用一套主题风格:

假如某个项目需要使用蓝色主题,那么我们可能会在main.js中这样从theme-blue下引入对应的css文件:
...
import '../assets/theme-blue/index.css';
...
然后我们执行打包命令,蓝色主题下的样式文件就会被引入,于是项目就应用了蓝色主题。
假如现在另一个项目需要生成一套红色主题该怎么办呢?
非常简单,把上面的代码改成从红色主题下引入文件即可:
import '../assets/theme-red/index.css';
这样的做法我们可能已经司空见惯了。但是为了应用不同主题而直接修改源代码是非常不好的方式,它会导致代码的稳定性急剧下降。
我们有更好的做法:配置打包命令。下面我们来介绍如何仅通过配置打包命令来生成多套主题样式(关于打包命令的基本说明请参考我之前的博客:多页vue应用的单页面打包方法(内含打包模式的应用))。
1. 新增打包命令
我们首先打开package.json,向script字段添加两个新的命令,分别是:build-blue和build-red,我们将会分别用这两个命令打包蓝色主题和红色主题。命令如下:
{
...
"scripts": {
...
"build-blue": "vue-cli-service build --mode themeBlue",
"build-red": "vue-cli-service build --mode themeRed"
}
}
前文我们说过,--mode后面的参数指定的是打包模式,这里我们分别为两个命令应用themeBlue和themeRed这两个打包模式。
那么这两个模式到底是什么呢?我们还没有定义。接下来我们就去定义这两个模式。
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中配置的别名以~和$开头时,路径无法被正确解析(其他特殊字符未完全测试,是否合法以实际效果为准),因此请避免在别名中以这两个字符开头。
本文介绍了一种通过配置打包命令实现多主题切换的方法,避免了直接修改代码带来的稳定性问题。通过定义不同打包模式下的环境变量,配合路径别名,实现了根据不同打包命令自动引入对应主题样式的目标。
1万+

被折叠的 条评论
为什么被折叠?



