Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack 4 新增 sideEffects 配置的作用和注意事项 #23

Open
yinxin630 opened this issue Aug 9, 2019 · 0 comments
Open

webpack 4 新增 sideEffects 配置的作用和注意事项 #23

yinxin630 opened this issue Aug 9, 2019 · 0 comments
Labels

Comments

@yinxin630
Copy link
Owner

yinxin630 commented Aug 9, 2019

在 webpack 2 版本, 增加了对 ES Module 的支持, 使得 webpack 能够分析出未使用的 export 内容, 然后将其 tree-shrking 掉

但是模块中那些具备副作用的代码, webpack 会将其保留

举一个例子, 项目中存在 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 模式下打包. 结果如下所示, 我去掉了无关的 webpack 启动代码

// output
([
  function(e, t, r) {
    'use strict';
    r.r(t),
      console.log('======== b.js =========='),
      console.log('aaaaaaaaaaaaa');
  },
])

打包结果中, 不包含 b 模块, 但是 b.js 中的副作用代码被保留了, 这是合乎情理的

sideEFfects 作用

下面修改下 b.js 的内容

// 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());
}

我们在 Array 原型链上定义了一个新方法 sum, 这是具有副作用的. 然后在 b 模块中调用了该方法, 但是作为 b 模块的维护者, 我又希望 sum 是"纯粹"的, 只被我使用, 外部并不依赖它的实现

修改 package.json, 新增字段 "sideEffects": false, 该字段表明整个工程是"无副作用"的
重新调用 webpack 编译, 期待在 b 模块没被使用的情况下, b 中定义的 sum 方法也被 tree-shaking 掉, 结果如下

([
  function(e, t, r) {
    'use strict';
    r.r(t), console.log('aaaaaaaaaaaaa');
  },
])

如期望那样, 整个 b 模块都被 tree-shaking 掉了, 包括包含副作用的代码

所以, sideEffects 可以优化打包体积, 并且一定程度上可以减少 webpack 对源码分析过程, 加快打包速度

你可以再试试引用 b 模块、sideEffects 值设为 true、去掉 sideEffects 等情况的打包结果

sideEffects 配置

sideEffects 除了能设置 boolean 值, 还可以设置为数组, 传递需要保留副作用的代码文件(例如: "./src/polyfill.js") 或者传递模糊匹配符(例如: "src/**/*.css")

sideEffects: boolean | string[]

sideEffects 注意事项

实际项目中, 通常并不能简单的设置为 "sideEffects": false, 有些副作用是需要保留的, 比如引入样式文件

webpack 会认为所有 import 'xxx' 语句是仅引入而未使用, 如果你错误的将其声明成了"无副作用", 它们就会被 tree-shaking 掉, 并且由于 tree-shaking 仅在 production 模式生效, 本地开发时可能一切仍是正常的, 并不能及时发现问题

下面这些都是"仅引入而未使用"的例子

import './normalize.css';
import './polyfill';
import './App.less';

相应的, 下面这种就不算

import icon from './icon.png';
function Icon() {
    return (
        <img src={icon} />
    )
}

这些有副作用的文件, 我们要正确声明, 修改 sideEffects 值

// package.json
"sideEffects": [
  "./src/**/*.css"
]

在使用中, 务必要正确设置 sideEffects 值

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 配置可能会很奇怪, 代码明明是有副作用的, 为什么要声明它是"无副作用"呢?

其实可以换个角度来想, sideEffects 是通知 webpack 该模块是可以安全的 tree-shaking 的, 无需关心其副作用

后话

sideEffects 对 webpack 构建过程有着很大影响, 对开发 npm 模块尤为重要. 使用中要特别注意声明的正确性

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant