前言
一般在项目开发中,代码部署都会有多种环境(比如测试环境、预发布环境、正式环境等),而一些代码需要根据环境不同来加载。举个实际例子,在项目的 index.html 中需要引入第三方的前端监控SDK。
js
<html>
<head>
<script src="https://cdn-go.cn/aegis/aegis-sdk/latest/aegis.min.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
以上的写法在多个环境中,SDK 都会进行日志上报,而在实际场景中往往只需要正式环境上报。虽然 SDK 可以配置上报域名,把其他环境的日志过滤掉。但这样就会有个不太优雅的点,既然其他环境不需要 SDK,为什么它还需要加载?能不能彻底让这段 script 消失掉?
本文就以该例子作为案例,分享在开发中如何工程化的根据环境去加载代码的解决方案。大概思路是通过获取构建时的命令行参数,结合 html-webpack-plugin 的模板语法对代码进行处理。
命令参数配置
注入环境参数
既然需要根据不同环境判断,那么就要在构建时为命令注入对应的环境参数。
package.json:
js
"scripts": {
"build": "npm run build:prod",
"build:dev": "node build/build.js --env dev",
"build:test": "node build/build.js --env test",
"build:preprod": "node build/build.js --env preprod",
"build:prod": "node build/build.js --env prod"
}
这段 package.json
配置包含了一组用于构建项目的命令。每项对应着不同环境在构建时需要执行的命令,通过 --env
进行传参。
获取命令中的参数
接下来需要借助一个 npm 包帮助我们更便捷的获取参数。commander
是一个用于构建命令行界面(CLI)的 Node.js 模块。它提供了一种简单和灵活的方式来创建和管理命令行,使开发者能够轻松地定义命令、选项和参数。
安装它:
bash
npm install commander -D
定义一个通用获取命令行参数的文件,命名为 program-argv.js
:
js
const { program } = require('commander')
program
.option('-e --env [type]', 'environment type', 'prod')
.parse(process.argv)
module.exports = program.opts()
-
option 方法对应的参数解释:
'-e --env [type]'
:这里指定了选项的短形式 -e 和长形式 --env,以及可选的参数 [type],表示环境类型。'environment type'
:这是选项的描述。'prod'
:这是选项的默认值,如果未提供环境类型参数,则默认为 prod。
-
parse 方法会让 commander 库解析传递给命令行参数(process.argv)。
-
opts 方法用于获取已解析的命令行选项的值。
下面我们就可以在 build 的时候引用该文件进行获取命令行参数:
js
const program = require('./program-argv.js')
console.log(program.env)
当我们使用 npm run build
的时候,上面 log 就会输出 prod。
html-webpack-plugin 配置
前置知识
以下是一段简单在 webpack 内配置 html-webpack-plugin 的例子,我们配置 template 对 index.html 进行处理,并传入 title 参数。
js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')
module.exports = {
entry: 'index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index.js',
},
plugins: [new HtmlWebpackPlugin({
filename: `index.html`,
template: `./src/index.html`,
title: '首页'
})]
}
在 index.html 中可以使用 html-webpack-plugin 提供的模板语法引用 title 参数。
html
<html>
<head>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="app"></div>
</body>
</html>
根据环境变量加载代码
好了,有了以上的知识。下面开始进入正题,步骤很简单,只需要把前面的知识整合起来即可。
首先引用环境变量的文件,将环境变量作为参数传入插件。
js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const path = require('path')
const program = require('./program-argv.js')
module.exports = {
entry: 'index.js',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'index.js',
},
plugins: [new HtmlWebpackPlugin({
filename: `index.html`,
template: `./src/index.html`,
title: '首页',
// 传入构建时的环境变量
buildEnv: program.env
})]
}
在 index.html 使用模板语法判断环境变量按需加载代码:
html
<html>
<head>
<% if (htmlWebpackPlugin.options.buildEnv === 'prod') { %>
<script src="https://cdn-go.cn/aegis/aegis-sdk/latest/aegis.min.js"></script>
<% } %>
</head>
<body>
<div id="app"></div>
</body>
</html>
当构建时的命令行参数为 prod 时,打包输出的 index.html 文件中就会有这段 script 标签,其他环境打包时就会彻底去掉这段代码。
在有 vue-ci 的项目中,配置方式会有些不同,需要使用 chainWebpack 为 html-webpack-plugin 传入参数:
javascript
const program = require('./program-argv.js')
module.exports = {
// ...
chainWebpack: (config) => {
config
.plugin('html')
.tap(args => {
args[0].buildEnv = program.env
return args
})
}
}
}
总结
本文通过工程化的方式,在构建时使用 commander 获取命令行变量,配合 html-webpack-plugin 的模板语法对文件代码进行处理,达到按需加载代码的效果。总的来说操作和理解起来并不难,就能得到一个优雅加载代码的小技巧。除了本文所讲的应用场景,还可发散思维使用这种方式应用其他的场景。