在一个库的 package.json 看到了 sideEffects 这个字段,好奇它的作用,查资料可得:
webpack v4 开始新增了一个
sideEffects
特性,通过给package.json
加入sideEffects
声明该 包/模块 是否包含 sideEffects(副作用),从而可以为 tree-shaking 提供更大的优化空间。
主要意思就是,加了这个字段,打包工具可以更好地为 tree-shaking 工作。让我们来看看它是如何工作的。sideEffects 对 webpack 构建过程有着很大影响, 对 npm 模块尤为重要. 使用中要特别注意声明的正确性。
以下转载于:https://libin1991.github.io/,样例代码:https://github.com/JexLau/demo/side-effects
🌰 小例子
项目存在 utils/a.js
, utils/b.js
, utils/index.js
三个文件,其中 b 模块包含一条打印语句, 是具有副作用的。
// utils/a.js
export function a() {
console.log('aaaaaaaaaaaaa');
}
// utils/b.js
console.log('======== b.js ==========');
export function b() {
console.log('bbbbbbbbbbbbbb');
}
// utils/index.js
export * from './a';
export * from './b';
添加主入口 app.js
, 只引用 a 模块, 期望未使用的 b 模块被 tree-shaking 掉
// app.js
import { a } from './utils';
a();
看一下打包后的结果, 注意要在 production 模式下打包。结果如下所示:打包结果中, 不包含 b 模块, 但是 b.js
中的副作用代码被保留了, 这是合乎情理的。
// output
function a() {
console.log("aaaaaaaaaaaaa");
}
console.log("======== b.js ==========");
a();
🪬 sideEFfects 作用
下面修改下 b.js
的内容,在Array 原型链上定义了一个新方法 sum
, 这是具有副作用的. 然后在 b 模块中调用了该方法, 但是作为 b 模块的维护者, 我又希望 sum
是”纯粹”的, 只被我使用, 外部并不依赖它的实现:
// utils/b.js
Object.defineProperty(Array.prototype, 'sum', {
value: function() {
return this.reduce((sum, num) =sum += num, 0);
}
})
export function b() {
console.log([1, 2, 3, 4].sum());
}
没添加sideEffects
之前:
!function(){
"use strict";
Object.defineProperty(Array.prototype,"sum",{value:function(){return this.reduce(((a,e)=>a+e),0)}}),
console.log("aaaaaaaaaaaaa")
}();
添加字段 "sideEffects": false
, 该字段表明整个工程是”无副作用”的。重新调用编译, 期待在 b 模块没被使用的情况下, b 中定义的 sum 方法也被 tree-shaking 掉, 结果如下:
!function(){"use strict";console.log("aaaaaaaaaaaaa")}();
如期望那样, 整个 b 模块都被 tree-shaking 掉了, 包括包含副作用的代码。
所以, sideEffects 可以优化打包体积, 并且一定程度上可以减少 webpack 对源码分析过程, 加快打包速度
sideEffects 配置
sideEffects 除了能设置 boolean 值, 还可以设置为数组, 传递需要保留副作用的代码文件(例如: “./src/polyfill.js”) 或者传递模糊匹配符(例如: “src/*/.css”)
sideEffects 注意事项
实际项目中, 通常并不能简单的设置为 "sideEffects": false
, 有些副作用是需要保留的, 比如引入样式文件。
webpack 会认为所有 import 'xxx'
语句是仅引入而未使用, 如果你错误的将其声明成了”无副作用”, 它们就会被 tree-shaking 掉, 并且由于 tree-shaking 仅在 production 模式生效, 本地开发时可能一切仍是正常的, 生产环境并不能及时发现问题。
下面这些都是”仅引入而未使用”的例子:
import './normalize.css';
import './polyfill';
import './App.less';
这些有副作用的文件, 我们要正确声明, 修改 sideEffects 值:
// package.json
"sideEffects": [
"./src/**/*.css"
]
sideEffects 局限性
sideEffects 配置是以文件为维度的, 只要你配置了文件具备副作用, 即便你只用了该文件中没有副作用的那部分功能, 仍然会将副作用保留。
比如将 b.js
修改为
Object.defineProperty(Array.prototype, 'sum', {
value: function() {
return this.reduce((sum, num) =sum += num, 0);
}
})
export function b() {
console.log([1, 2, 3, 4].sum());
}
export function c() {
console.log('ccccccccccccccccccc');
}
在 app.js
中仅引入 c 方法, b 方法会被 tree-shaking, 但 sum 方法不会。
从这个角度来看,sideEffects 是通知 webpack 该模块是可以安全的 tree-shaking 的, 无需关心其副作用。