原文连接vue
webpack v4 开始新增了一个 sideEffects
特性,经过给 package.json
加入 sideEffects: false
声明该包模块是否包含 sideEffects(反作用),从而能够为 tree-shaking 提供更大的优化空间。react
先看张图感觉一下:webpack
注:v4 beta 版时叫 pure module
, 后来改为了 sideEffects
git
基于咱们对 fp 中的 side effect 的理解,咱们能够认为,只要咱们肯定当前包里的模块不包含反作用,而后将发布到 npm 里的包标注为 sideEffects: false
,咱们就能为使用方提供更好的打包体验。原理是 webpack 能将标记为 side-effects-free 的包由 import {a} from xx
转换为 import {a} from 'xx/a'
,从而自动修剪掉没必要要的 import,做用同 babel-plugin-import。github
因而很愉快的我给个人几个库都加上了这个配置(肯定都不含反作用)。web
直到我几个月前看到 @Sean Larkin 给 vue 提交了这样一个 pr:chore(package.json): Add sideEffects: false field in package.json, 当时我就有点疑惑,依我对 vue 的了解,代码里的反作用挺多啊,好比不少函数都有对 Vue.prototype
的引用甚至修改,应该不能设置 sideEffects: false
才对啊。然而事实是我被打脸了,由于尤大很快的合并了这个 pr。。这直接致使我不敢给 mobx 加上这个配置,由于已经彻底不明白 webpack 的这个 sideEffects 指的是什么了。。npm
直到前两天有人给 mobx-utils 提了 issue 说能够加上这个配置帮助 tree shaking,疑惑中我想起了 vue 的那个 pr 又翻出来看了一遍,发如今 pr 下已经有人跟我提了同样的疑问:json
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)babel
I hope you can help me understand how this works.
Sean 原来的 pr 里是这样写的:
This PR adds the"sideEffects": false
property in vue'spackage.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 的背景就不介绍了想必不少人都了解,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 ( <div> ... { process.env.NODE_ENV === 'production' ? null : <DevTools /> } </div> ); } }
这是一个很常见的按需导入场景,然而在没有 sideEffects: false
配置时,即使 NODE_ENV
设为 production
,打包后的代码里依然会包含 mobx-react-devtools
包,虽然咱们没使用过其导出成员,可是 mobx-react-devtools
仍是会被 import,由于里面“可能”会有反作用。但当咱们加上 sideEffects false 以后,tree shaking 就能安全的把它从 bundle 里完整的移除掉了。
上面也说到,一般咱们发布到 npm 上的包很难保证其是否包含反作用(多是代码的锅多是 transformer 的锅),可是咱们基本能确保这个包是否会对包之外的对象产生影响,好比是否修改了 window 上的属性,是否复写了原生对象方法等。若是咱们能保证这一点,其实咱们就能知道整个包是否能设置 sideEffects: false
了,至因而不是真的有反作用则并不重要,这对于 webpack 而言都是能够接受的。这也就能解释为何能给 vue 这个自己充满反作用的包加上 sideEffects: false
了。
因此其实 webpack 里的 sideEffects: false
的意思并非我这个模块真的没有反作用,而只是为了在摇树时告诉 webpack:我这个包在设计的时候就是指望没有反作用的,即便他打完包后是有反作用的,webpack 同窗你摇树时放心的当成无反作用包摇就好啦!。
也就是说,只要你的包不是用来作 polyfill 或 shim 之类的事情,就尽管放心的给他加上 sideEffects: false
吧!