tree-shaking實(shí)戰(zhàn)

tree-shaking是一個在前端領(lǐng)域比較熟知的東西了弧轧。在沒有深入了解前娱局,一直以為他在項目中發(fā)揮了很大的作用扣汪。但是在看了許多文章說tree-shaking并沒有什么卵用后,想自己深入了解一下科平,所以搜了許多博文褥紫,自己也在項目中試驗(yàn)了一下〉苫郏基本了解了大致的流程髓考。所以這篇博文主要是記錄一下學(xué)習(xí)的成果。

tree-shaking是干啥的:

// app.js

export function A(a, b) {
    return a + b
}

export function B(a, b) {
    return a + b
}

// index.js

import {A} from '/app.js'

A(1, 2)

當(dāng)index.js引用了app.js的A函數(shù)弃酌,如果tree-shaking起了作用氨菇,B函數(shù)是不會被打包進(jìn)最后的bundle的。

但是

世界上有很多但是妓湘,而且往往但是后面的內(nèi)容更加重要查蓉。

relies on the static structure of ES2015 module syntax, i.e. import and export.

在webpack官網(wǎng)當(dāng)中有這樣一句話,翻譯成人話就是tree-shaking依賴es6的模塊引入或輸出語法榜贴。如果你的模塊引入方式是require等等等亂七八糟的東西豌研。tree-shaking將不會起到任何作用。


babel, webpack打包, uglifyJs

這三項東西東西是在我們開發(fā)中幾乎繞不過去東西唬党。而tree-shaking的關(guān)鍵點(diǎn)就在第一步鹃共,babel

雖然我不太了解webpack內(nèi)部的運(yùn)行機(jī)制(看過運(yùn)行順序的相關(guān)文章,但一直是懵比狀態(tài))驶拱,但是看過這么多的文章后霜浴,上面三項的基本運(yùn)行順序還是理解的:

就是babel-loader先去處理js文件,處理過后蓝纲,webpack進(jìn)行打包處理阴孟,最后uglifyjs進(jìn)行代碼壓縮晌纫。而關(guān)鍵就是babel怎么去處理js文件

babel的配置文件中有一個preset配置項:


{
  "presets": [
    ["env", {
      "modules": false  //關(guān)鍵點(diǎn)
    }],
    "stage-2",
    "react"
  ]
}


其中presets里面的env的options中有一個 modules: false,這是指示babel如何去處理import和exports等關(guān)鍵子,默認(rèn)處理成require形式永丝。如果加上此option锹漱,那么babel就不會吧import形式,轉(zhuǎn)變成require形式类溢。為webpack進(jìn)行tree-shaking創(chuàng)造了條件凌蔬。

在看過這些篇博文后露懒,我本人對于tree-shaking有了一個基本的認(rèn)識闯冷,那就是

babel首先處理js文件,真正進(jìn)行tree-shaking識別和記錄的是webpack本身懈词。刪除多于代碼是在uglify中執(zhí)行的

注:webpack在認(rèn)定某塊代碼無用后蛇耀,會再處理過程中寫下一段注釋。uglifyjs會根據(jù)這點(diǎn)注釋去進(jìn)行刪除代碼坎弯。

直接復(fù)制這篇博文代碼

注釋的大體內(nèi)容(博文很久了纺涤,還是在webpack2.0時代,具體內(nèi)容可能已經(jīng)變化抠忘,但原理應(yīng)該是不變的撩炊。)


function(module, exports, __webpack_require__) {
    /* harmony export */ exports["foo"] = foo;
    /* unused harmony export bar */;

    function foo() {
        return 'foo';
    }
    function bar() {
        return 'bar';
    }
}

tree-shaking,實(shí)戰(zhàn)代碼:

背景:在學(xué)習(xí)tree-shaking的過程中崎脉,如何支持class的tree-shaking是我一直關(guān)注的拧咳,而且大部分的文章還只停留在理論方面。所以最近自己寫了一個demo,支持class的tree-shaking

1.首先使用loader去處理囚灼,實(shí)驗(yàn)階段代碼骆膝,編譯成標(biāo)準(zhǔn)es代碼。這樣webpack內(nèi)部的編譯器才能正確識別代碼灶体。


module: {
rules: [
  {
    test: /\.js$/,
    use: [
      {
        loader: 'babel-loader',
        options: {
          presets: ['babel-preset-stage-2', 'babel-preset-react']
        }
      },
      'eslint-loader'
    ],
    exclude: /node_modules/
  }
]
}

2.然后通過webpack打包阅签,并對代碼進(jìn)行tree-shaking.在打包完最后的bundle之后,和輸出文件之前蝎抽,對最后的bundle進(jìn)行兼容性處理政钟。

plugins: [
    new UglifyJSPlugin(),  //  uglify要在babelPugin的前面
    new BabelPlugin({  //在這個插件內(nèi)部進(jìn)行最后bundle的兼容性處理
      test: /\.js$/,
      babelOptions: {
        presets: [env]
      }
    }),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production')
    }),
    new HtmlWebpackPlugin({
      template: 'index.html'
    }),
  ]


最后總結(jié)步驟:

  1. 先編譯實(shí)驗(yàn)性質(zhì)代碼為標(biāo)準(zhǔn)代碼,會涉及到babel-preset-stage-x插件

  2. webpack打包代碼并進(jìn)行tree-shaking識別樟结。

  3. uglifyjs進(jìn)行代碼壓縮锥涕,并根據(jù)webpack標(biāo)識刪除多余代碼

4.對最后的代碼進(jìn)行兼容性處理涉及到babel-preset-env插件。

第三方類庫的tree-shaking

在研究了許多第三方類庫后狭吼,基本得出了一個結(jié)論:tree-shaking本質(zhì)上是不能對大部分的第三方類庫進(jìn)行tree-shaking的.上面的實(shí)戰(zhàn)代碼层坠,對于自己寫的代碼還有點(diǎn)用,但是只要涉及到第三方類庫刁笙,基本就是歇菜破花。

ramda的輸出文件:
大部分的react ui組件谦趣,以及函數(shù)工具類庫∽浚基本都是這樣來進(jìn)行模塊輸出前鹅,和引用的。


export { default as F } from './F';
export { default as T } from './T';
export { default as __ } from './__';
export { default as add } from './add';
export { default as addIndex } from './addIndex';
export { default as adjust } from './adjust';
export { default as all } from './all';
export { default as allPass } from './allPass';
export { default as always } from './always';
export { default as and } from './and';
export { default as any } from './any';
export { default as anyPass } from './anyPass';
export { default as ap } from './ap';
export { default as aperture } from './aperture';
export { default as append } from './append';
export { default as apply } from './apply';
export { default as applySpec } from './applySpec';

...

這樣的文件結(jié)構(gòu)是無法進(jìn)行tree-shaking的


// 只要是你在代碼中引用了一個方法峭梳,那么你肯定將所有的代碼都引入了進(jìn)來
import {path} from 'ramda' 


唯一的解決方法就是直接到具體的文件夾去引用舰绘,而不是在根index.js里面去引用。


import path from 'ramda/src/path'


但是如果每一次引用都是這樣去寫葱椭,開發(fā)的效率就無法保證捂寿,所以基本上有點(diǎn)追求的技術(shù)團(tuán)隊,基本上會再類庫的基礎(chǔ)上孵运,開發(fā)一個babel的插件以支持代碼的tree-shaking秦陋。

像著名的antd,以及ramda等都開發(fā)了相應(yīng)的插件治笨。

babel-plugin-ramda:此插件會默認(rèn)將你寫的代碼轉(zhuǎn)化為tree-shaking的代碼


from:

import {path} from 'ramda' 

to

import path from 'ramda/src/path'


而本人也在了解了以上東西后驳概,為本公司的ui組件開發(fā)了一個插件:

babel-plugin-b-rc

在看過ramda的代碼,以及他的構(gòu)建過程和對于tree-shaking的支持后旷赖,學(xué)到了很多顺又。有的時候?qū)W習(xí)webpack以及如何優(yōu)化網(wǎng)頁的時候,不知道如何下手等孵。我的經(jīng)驗(yàn)就是找到一個技術(shù)點(diǎn)稚照,進(jìn)行深入。比如我得研究點(diǎn)就是tree-shaking流济,在一路研究過后锐锣,babel,babel-preset, babel-plugin,webpack,webpack-plugin,都有了一定的了解绳瘟。這幾個功能點(diǎn)基本上花費(fèi)了我將近一個多月的時間雕憔。從找材料,到動手完成自己的一個babel插件糖声,收獲頗豐斤彼。學(xué)習(xí)babel以及webpack的過程,其本質(zhì)是學(xué)習(xí)如何優(yōu)化網(wǎng)頁的過程U盒骸A鹞!悦施!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末并扇,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子抡诞,更是在濱河造成了極大的恐慌穷蛹,老刑警劉巖土陪,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異肴熏,居然都是意外死亡鬼雀,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門蛙吏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來源哩,“玉大人,你說我怎么就攤上這事鸦做±常” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵馁龟,是天一觀的道長崩侠。 經(jīng)常有香客問我漆魔,道長坷檩,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任改抡,我火速辦了婚禮矢炼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘阿纤。我一直安慰自己句灌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布欠拾。 她就那樣靜靜地躺著胰锌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪藐窄。 梳的紋絲不亂的頭發(fā)上资昧,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音荆忍,去河邊找鬼格带。 笑死,一個胖子當(dāng)著我的面吹牛刹枉,可吹牛的內(nèi)容都是我干的叽唱。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼微宝,長吁一口氣:“原來是場噩夢啊……” “哼棺亭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蟋软,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤镶摘,失蹤者是張志新(化名)和其女友劉穎专甩,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钉稍,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涤躲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了贡未。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片种樱。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖俊卤,靈堂內(nèi)的尸體忽然破棺而出嫩挤,到底是詐尸還是另有隱情,我是刑警寧澤消恍,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布岂昭,位于F島的核電站,受9級特大地震影響狠怨,放射性物質(zhì)發(fā)生泄漏约啊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一佣赖、第九天 我趴在偏房一處隱蔽的房頂上張望恰矩。 院中可真熱鬧,春花似錦憎蛤、人聲如沸外傅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽萎胰。三九已至,卻和暖如春棚辽,著一層夾襖步出監(jiān)牢的瞬間技竟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工晚胡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留灵奖,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓估盘,卻偏偏與公主長得像瓷患,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子遣妥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353