原文連接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
吧!