神代綺凛の随波逐流

让 Vue 使用指定配置文件进行构建

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »

Head Pic: #凯尔希 「梦想」 by キョウケン - pixiv

在 Vue 项目中,npm script 里通常用vue-cli-service build来构建成品,该命令默认会使用vue.config.js作为配置文件

但如果我们有在一个 Vue 项目中使用多个配置文件来构建不同成品的需求,该怎么办

可惜非常不人性化的是,vue-cli-service并没有设计支持使用类似-c configFile这样的参数来指定使用其它配置文件,那么还有其它办法么

VUE_CLI_SERVICE_CONFIG_PATH环境变量

还好,经过搜索,我发现vue-cli-service是支持使用VUE_CLI_SERVICE_CONFIG_PATH环境变量来自定义配置文件的,我们可以添加一个 npm script 像下面这样(Unix 下)

VUE_CLI_SERVICE_CONFIG_PATH=my.vue.config.js vue-cli-service build

然而我实际执行确报错“找不到模块my.vue.config.js

看了下vue-cli-service源码,获取配置文件的部分是这样的

// In loadUserOptions()
const configPath = (
  process.env.VUE_CLI_SERVICE_CONFIG_PATH ||
  path.resolve(this.context, 'vue.config.js')
)
if (fs.existsSync(configPath)) {
  try {
    fileConfig = require(configPath)

    if (typeof fileConfig === 'function') {
      fileConfig = fileConfig()
    }

    if (!fileConfig || typeof fileConfig !== 'object') {
      error(
        `Error loading ${chalk.bold('vue.config.js')}: should export an object or a function that returns object.`
      )
      fileConfig = null
    }
  } catch (e) {
    error(`Error loading ${chalk.bold('vue.config.js')}:`)
    throw e
  }
}

可以看到,configPath会直接取环境变量作为 id 来require,而不会像默认的vue.config.js一样根据上下文来计算绝对路径

虽然看似非常反人类,但仔细想想也能理解这是出于安全考虑

那么解决方法就显而易见了,传个绝对路径进去,例如在 Unix 下可以利用$PWD环境变量

VUE_CLI_SERVICE_CONFIG_PATH=$PWD/my.vue.config.js vue-cli-service build

但这衍生出另一个问题,最直接的说,Windows 下没有$PWD,而是%cd%,虽然我们能利用cross-env解决不同操作系统下环境变量设置方式不同的问题,却无法解决 Windows 与 Unix 系统环境变量获取方式不同的问题

既然是 node,就用 node 解决

一拍大腿之后,曲线救国的方案就出来了,我们可以直接用 node 获取配置的绝对路径,然后作为环境变量来执行npm run build

在 node 中执行命令就要请child_process出场了,梳理一下我们的需求:

  1. 执行npm run build
  2. 传入VUE_CLI_SERVICE_CONFIG_PATH环境变量
  3. 维持原本的输出形式

阅读其文档,最终我选用 spawnSync 方法,相关参数如下

child_process.spawnSync(command[, args][, options])

值得说明的是 options.stdio 这一参数,其中有一条说明

'inherit': Pass through the corresponding stdio stream to/from the parent process. In the first three positions, this is equivalent to process.stdin, process.stdout, and process.stderr, respectively. In any other position, equivalent to 'ignore'.

这正是我们想要的,将子进程的输入输出原封不动的传回给父进程,这样一来体验就与直接执行命令无异(指各种命令行动画和文字颜色这种)

所以最终我们可以像下面这样写个,比如说myBuild.js

const { spawnSync } = require('child_process');
const { resolve } = require('path');

spawnSync('npm', ['run', 'build'], {
  shell: true,
  env: {
    ...process.env, // 要记得导入原本的环境变量
    VUE_CLI_SERVICE_CONFIG_PATH: resolve(__dirname, 'my.vue.config.js'),
  },
  stdio: 'inherit',
});

然后我们就可以添加一个 npm script 了,"build:my": "node myBuild.js"

拓展

我们可以做的更通用化一点,接受一个参数来指定配置文件,做到可以写成"build:my": "node build.js my.vue.config.js"这样的效果,这里就不详细说了