有些时候我们希望某些代码只在特定环境中能够执行。前面讲到,代码压缩工具会将死代码(if(false))删除,因此,我们可以基于这个特性,通过 DefinePlugin 定义一些环境变量,来将类似

if (process.env.NODE_ENV === "development") {
  console.log("Hello during development");
}
1
2
3

这样的代码转换成 if(true) 或者 if(false) 的形式。

从 Webpack 4 开始,process.env.NODE_ENV 默认根据 mode 参数设置,但是仅限于 Webpack 内部。如果想要将 process.env.NODE_ENV 传给给其他工具,则需要在 Webpack 外或者在 Webpack 配置文件中定义。

除了使用 process.env.NODE_ENV, 我们还可以通过 webpack.EnvironmentPlugin(["NODE_ENV"]) 读取环境变量,底层基于 DefinePlugin

dotenv-webpackopen in new window 从一个 .env 文件中读取配置,然后内部使用 DefinePlugin 来定义环境变量。

DefinePlugin 的基础用法

有下面这段代码:

var foo;
if (foo === 'bar') console.log('bar'); // Not free
if (bar === 'bar') console.log('bar'); // Free
1
2
3

如果将 bar 替换成字符串 "foobar",则代码如下:

var foo;
if (foo === 'bar') console.log('bar'); // Not free
if ('foobar' === 'bar') console.log('bar');
1
2
3

在进行代码压缩的时候,通过代码分析,上面的代码与下面这段等价:

var foo;
if (foo === 'bar') console.log('bar'); // Not free
if (false) console.log('bar');
1
2
3

则最终生成代码为:

var foo;
if (foo === 'bar') console.log('bar'); // Not free
1
2

基于条件的删除代码是 DefinePlugin 的核心能力。代码压缩器会分析代码,并将无用代码完全删除。

在 Babel 中,babel-plugin-transform-defineopen in new window 有着同样的功能。

设置 process.env.NODE_ENV

因为 Webpack 是直接替换变量文本,因此在定义环境变量值的时候,我们需要 JSON.stringify 一下。此时,环境变量的值为 "demo",然后 Webpack 会逐个替换找到的文本。

webpack.parts.js

exports.setFreeVariable = (key, value) => {
  const env = {};
  env[key] = JSON.stringify(value);

  return {
    plugins: [new webpack.DefinePlugin(env)],
  };
};
1
2
3
4
5
6
7
8

webpack.config.js

const commonConfig = merge([
  ...parts.setFreeVariable('HELLO', 'hello from config'),
]);
1
2
3

在应用代码中:

src/component.js


// export default (text = "Hello world") => {
export default (text = HELLO) => {
  const element = document.createElement("div");
  ...
};
1
2
3
4
5
6

此时运行代码,可以看到按钮的文案发生了变化。

根据环境变量加载不同的模块

我们可以借助于 DefinePlugin 来有条件的加载某些模块。比如有如下代码:

.
└── store
    ├── index.js
    ├── store.dev.js
    └── store.prod.js
1
2
3
4
5

index.js 中:

if (process.env.NODE_ENV === 'production') {
  module.exports = require('./store.prod');
} else {
  module.exports = require('./store.dev');
}
1
2
3
4
5

此时,我们通过 DefinePlugin 定义 NODE_ENV 来加载不同的模块。值得注意的是,这里只能使用 CommonJS 模块语法,因为 ES2015 不支持动态导入模块。

总结

Webpack 可以通过 DefinePluginEnvironmentPlugin 来定义环境变量。EnvironmentPlugin 同时还会将系统环境变量也添加进来。

DefinePlugin 会基于 Webpack 的分析进行自由变量替换。在 Babel 中也有类似的功能。

一些代码压缩工具会将死代码删除,因此我们可以借助于 DefinePlugin 来生成死代码,从而将这些代码从构建输出结果中删除。

DefinePlugin 还可以被应用在模块级别,通过一个包装函数,我们可以有条件的决定使用哪个模块。

关注微信公众号,获取最新推送~