Tree Shaking我原來也只是了解,這次碰巧深入研究了下,就寫個博客記錄一下,網上有很多講Tree Shaking的,我寫的這篇跟他們側重點不一樣
Tree Shaking相關的基礎知識
1 webpack會從入口文件開始不斷的獲取你的依賴,就像一顆樹一樣從根節點開始不斷往下延伸,只有被依賴的文件才會加入樹,注意這不叫Tree Shaking,Tree Shaking是指依賴的文件只需要的一部分,則把不需要的部分代碼搖掉
2 Tree Shaking只能識別es6語法,這點很關鍵(注意去掉polyfill的模塊降級),但是webpack不支持es6打包,也就是說你用import寫的代碼可以被Tree Shaking,但是你npm別人的庫,如果別人不是es寫法,那么無法Tree Shaking。比如lodash和lodash-es,后者是es的導入寫法,代碼就可以被壓縮。
3 Tree Shaking原來是用uglifyjs來做壓縮的,現在由於現在不維護uglify-es了(uglify-es is no longer maintained ),現在改用terser了,據說是uglify的一個分支。(對我們使用來說其實沒什么感覺,只是了解一下)
4 什么是副作用,之前看別人的文章看到懵懵的,后面終於理解了,其實很簡單
index.js 如下,就是導入了util.js
import './vendor/util'
vendor/util.js 如下,里面有做全局的修改或者console.log什么東西,我這里就是給全局的window加了個屬性a,沒有導出東西來,卻使用了這個js文件,這就是副作用
// vendor/util.js
export function a() {
return 'this is function "a"';
}
export function b() {
return 'this is function "b"';
}
export function c() {
return 'this is function "c"';
}
window.a = 43
有時候我們寫代碼用這種寫法,由於里面沒有導入東西進來,webpack准確說是terser就不知道你到底要不要這個東西,萬一里面有修改全局的操作你把它丟了肯定不行,如果不丟那你就要去讀一遍文件才知道到底有沒有副作用,官方文檔說是非常耗時且因為js是動態語言所以是非常不可靠的操作,我這里僅僅是寫的非常簡單,工作中可能業務變得更加復雜。
Tree Shaking的基礎了解完了,接下來講下我讀webpack文檔的一些見解
先發2篇我讀過的關於Tree Shaking較好的文章
webpack官網講解Tree Shaking的文檔:https://webpack.js.org/guides/tree-shaking/#root
你的Tree-Shaking並沒什么卵用:https://segmentfault.com/a/1190000012794598
根據‘你的Tree-Shaking並沒什么卵用’ 這篇文章說UglifyJS完全不能識別副作用,但是我自己做了一些測試發現,目前的webpack4.40.2是可以識別大部分副作用的。
然后講下官網的文檔,之前說了terser識別副作用特別的耗時且不准確(官方舉例是react的hoc無法識別副作用),如果你能指定你的哪些文件有副作用或者所有文件都沒有副作用那就皆大歡喜了,於是在package.json里出了一個屬性sideEffects
接下來我做了一些測試關於sideEffects屬性,注意terser的Tree Shaking只在生產環境webpack會給你調用來壓縮代碼,dev是不會壓縮代碼自然不會Tree Shaking的
第一種情況,index和util都不變,sideEffects:false,指定所有的文件都沒有副作用
結果是terser完全不去管你這個文件里面寫的啥,就是說你給window加個屬性a沒有執行
第二種情況,index和util都不變,sideEffects:["./src/vendor/util.js"],指定util是有副作用
結果terser讀了util,知道了你做了什么處理,於是window加個屬性a成功執行
第三種情況,index和util都不變,不加sideEffects,不告訴terser哪些有副作用,叫它自己每個引用了的文件都去看下代碼
結果terser讀了util,知道了你做了什么處理,於是window加個屬性a成功執行
第四種情況,index變成如下代碼
import { a } from './vendor/util'
結果和index使用import './vendor/util'的每種情況一樣,也就是說你導入了a但是沒有使用,在terser看來就是import './vendor/util'差不多
第五種情況
index變成如下代碼
import { a } from './vendor/util'
console.log(a())
結果無論sideEffects怎么設置,window加個屬性a都會成功
通過幾次測試下來發現sideEffects屬性只是針對你從文件導入了方法卻沒有使用(或者你壓根沒有導入方法)的情況生效,從這么看來sideEffects屬性其實能做的優化不大,而且只是針對打包速度方面的優化(包已經打好了,訪問速度跟它沒有關系了),而且很坑,一個前端項目如果不只你一個人參與,你偷偷加個sideEffects屬性,然后代碼沒壓縮在dev跑的好好的,放到生產環境突然就失效了,假設人家不知道sideEffects,那抓破腦袋也不知道是咋回事(如果你就是想坑你同事,那你就可以happy了~)