资讯专栏INFORMATION COLUMN

Webpack 中的 sideEffects 到底该怎么用?

googollee / 3003人阅读

摘要:原文链接开始新增了一个特性,通过给加入声明该包模块是否包含副作用,从而可以为提供更大的优化空间。而通常我们期望的是模块既然不被使用了,其中所有的代码应该不被引入才对。但当我们加上之后,就能安全的把它从里完整的移除掉了。

原文链接

webpack v4 开始新增了一个 sideEffects 特性,通过给 package.json 加入 sideEffects: false 声明该包模块是否包含 sideEffects(副作用),从而可以为 tree-shaking 提供更大的优化空间。

先看张图感受一下:

注:v4 beta 版时叫 pure module, 后来改成了 sideEffects

基于我们对 fp 中的 side effect 的理解,我们可以认为,只要我们确定当前包里的模块不包含副作用,然后将发布到 npm 里的包标注为 sideEffects: false ,我们就能为使用方提供更好的打包体验。原理是 webpack 能将标记为 side-effects-free 的包由 import {a} from xx 转换为 import {a} from "xx/a",从而自动修剪掉不必要的 import,作用同 babel-plugin-import。

于是很愉快的我给我的几个库都加上了这个配置(确定都不含副作用)。

直到我几个月前看到 @Sean Larkin 给 vue 提交了这样一个 pr:chore(package.json): Add sideEffects: false field in package.json, 当时我就有点疑惑,依我对 vue 的了解,代码里的副作用挺多啊,比如很多函数都有对 Vue.prototype 的引用甚至修改,应该不能设置 sideEffects: false 才对啊。然而事实是我被打脸了,因为尤大很快的合并了这个 pr。。这直接导致我不敢给 mobx 加上这个配置,因为已经完全不明白 webpack 的这个 sideEffects 指的是什么了。。

直到前两天有人给 mobx-utils 提了 issue 说可以加上这个配置帮助 tree shaking,疑惑中我想起了 vue 的那个 pr 又翻出来看了一遍,发现在 pr 下已经有人跟我提了一样的疑问:

Hy Sean!

Could you please specify what you mean by "vue"s original source files"?

I looked at the index.js file in the src/core folder and to my knowledge there are plenty sideeffects that would be prune away by tree shaking. (e.g Object.defineProperty)

I hope you can help me understand how this works.

Sean 原来的 pr 里是这样写的:

This PR adds the "sideEffects": false property in vue"s package.json file. This allow"s webpack (for those who want to opt-in to requiring vue"s original source files (instead of the flattened esm bundles) and want to remove flow type through a babel-transform, then this will allow webpack to aggressively ignore and treeshake unused exports throughout the module system.

Sean 的意思是当你按需引入 vue 的源码文件而不是打包的 bundle 时,webpack 能帮助你做更好的 tree shaking。比如你这样引用 vue 中的模块:import Vue from "vue/src/core"

然后 Sean 就说此副作用非彼副作用(fp 中的),然后给了一个他在 stackoverflow 上的回答来解释 sideEffects,中心思想是:

whenever a module reexports all exports (regardless if used or unused) need to be evaluated and executed in the case that one of those exports created a side-effect with another.

每当一个模块重导出了所有导出(无论是否会被用) 需要被计算和执行时,其中一个导出就对其他的导出产生了副作用。

老实讲还是没懂。。有兴趣的看原答案:what-does-webpack-4-expect-from-a-package-with-sideeffects-false

翻完 官方文档 跟 官方 example,只是了解到有了 sideEffects 后 bundle 的变化,依然无法解释 webpack sideEffects 跟 fp 中的 sideEffect 有什么区别,进而也无法解释为什么 vue 明明很多副作用依然能配置 sideEffects: false ?

毛主席教导我们:自力更生,丰衣足食。

Tree Shaking 与副作用

Tree Shaking 的背景就不介绍了想必很多人都了解,webpack 的 tree shaking 的作用是可以将未被使用的 exported member 标记为 unused 同时在将其 re-export 的模块中不再 export。说起来很拗口,看代码:

// a.js
export function a() {}
// b.js
export function b(){}
// package/index.js
import a from "./a"
import b from "./b"
export { a, b }
// app.js
import {a} from "package"
console.log(a)

当我们已 app.js 为 entry 时,经过摇树后的代码会变成这样:

// a.js
export function a() {}
// b.js 不再导出 function b(){}
function b() {}
// package/index.js 不再导出 b 模块
import a from "./a"
import b from "./b"
export { a }
// app.js
import {a} from "package"
console.log(a)

配合 webpack 的 scope hoisting 和 uglify 之后,b 模块的痕迹会被完全抹杀掉。

但是如果 b 模块中添加了一些副作用,比如一个简单的 log:

// b.js
export function b(v) { reutrn v }
console.log(b(1))

webpack 之后会发现 b 模块内容变成了:

// b.js
console.log(function (v){return v}(1))

虽然 b 模块的导出是被忽略了,但是副作用代码被保留下来了。由于目前 transformer 转换后可能引入的各种奇怪操作引发的副作用(参考:你的Tree-Shaking并没什么卵用),很多时候我们会发现就算有了 tree shaking 我们的 bundle size 还是没有明显的减小。而通常我们期望的是 b 模块既然不被使用了,其中所有的代码应该不被引入才对。

这个时候 sideEffects 的作用就显现出来了:如果我们引入的 包/模块 被标记为 sideEffects: false 了,那么不管它是否真的有副作用,只要它没有被引用到,整个 模块/包 都会被完整的移除。以 mobx-react-devtool 为例,我们通常这样去用:

import DevTools from "mobx-react-devtools";

class MyApp extends React.Component {
  render() {
    return (
      
... { process.env.NODE_ENV === "production" ? null : }
); } }

这是一个很常见的按需导入场景,然而在没有 sideEffects: false 配置时,即便 NODE_ENV 设为 production ,打包后的代码里依然会包含 mobx-react-devtools 包,虽然我们没使用过其导出成员,但是 mobx-react-devtools 还是会被 import,因为里面“可能”会有副作用。但当我们加上 sideEffects false 之后,tree shaking 就能安全的把它从 bundle 里完整的移除掉了。

sideEffects 的使用场景

上面也说到,通常我们发布到 npm 上的包很难保证其是否包含副作用(可能是代码的锅可能是 transformer 的锅),但是我们基本能确保这个包是否会对包以外的对象产生影响,比如是否修改了 window 上的属性,是否复写了原生对象方法等。如果我们能保证这一点,其实我们就能知道整个包是否能设置 sideEffects: false了,至于是不是真的有副作用则并不重要,这对于 webpack 而言都是可以接受的。这也就能解释为什么能给 vue 这个本身充满副作用的包加上 sideEffects: false 了。

所以其实 webpack 里的 sideEffects: false 的意思并不是我这个模块真的没有副作用,而只是为了在摇树时告诉 webpack:我这个包在设计的时候就是期望没有副作用的,即使他打完包后是有副作用的,webpack 同学你摇树时放心的当成无副作用包摇就好啦!

也就是说,只要你的包不是用来做 polyfill 或 shim 之类的事情,就尽管放心的给他加上 sideEffects: false 吧!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/96250.html

相关文章

  • 带你了解webpack

    摘要:根据依赖关系,按照配置文件把模块函数分组打包成若干个。会随着自身的的修改,而发生变化。只需要在命令行运行时带上参数就搞定一些插件的废除和替换废弃了顶替者用属性变化压缩优化代码分割,下面详解还有一些新的插件,。 1. 前端工程化项目打包历史 前端工程化之前的时代略过 1. 半自动执行脚本来压缩合并文件 自从xmlhttprequest被挖掘出来,网页能够和服务端通讯,js能做的事越来越多...

    senntyou 评论0 收藏0
  • 带你了解webpack

    摘要:根据依赖关系,按照配置文件把模块函数分组打包成若干个。会随着自身的的修改,而发生变化。只需要在命令行运行时带上参数就搞定一些插件的废除和替换废弃了顶替者用属性变化压缩优化代码分割,下面详解还有一些新的插件,。 1. 前端工程化项目打包历史 前端工程化之前的时代略过 1. 半自动执行脚本来压缩合并文件 自从xmlhttprequest被挖掘出来,网页能够和服务端通讯,js能做的事越来越多...

    Tamic 评论0 收藏0
  • 前端性能优化—js代码打包

    摘要:注意使用的版本不同,可能会导致打包出的结果不一样。完整的优化代码见有用的文章分离第三方库及公用文件 现在的 web 应用,内容一般都很丰富,站点需要加载的资源也特别多,尤其要加载很多 js 文件。js 文件从服务端获取,体积大小决定了传输的快慢;浏览器端拿到 js 文件之后,还需要经过解压缩、解析、编译、执行操作,所以,控制 js 代码的体积以及按需加载对前端性能以及用户体验是十分的重...

    Rango 评论0 收藏0
  • webpack4介绍与总结

    摘要:随着承担地职责越来越大,模块化开发的需求越来越急迫。我们可以把当成是模块化标准的实现方案,但的功能不仅限于此。支持多种模块使用方式,包括的。下面介绍一下在工程中常用的。最后一个的输出就是我们最终要的结果。在文件有值的情况下,是必要的。 由于web应用扩展地得极其迅猛,前端技术也是日新月异,前端的苦不是有多难学,而是我刚学完,这东西就被淘汰了(手动哭脸)。框架方面我们有vue、react...

    yanbingyun1990 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<