淺談效能優化之Tree Shaking

scq000發表於2017-04-24

前端技術發展太快,感覺一天不努力就要被超越。這不, 最近在寫Angular2練習專案的時候,遇到了一個效能優化的技術點,叫做Tree Shaking。於是,我愉快地開始了學習之旅。

從技術實現上來說,它是依託於ES6提供的模組系統對程式碼進行靜態分析,並將程式碼中的死程式碼(dead code)移除的一種技術。因此,利用Tree Shaking技術可以很方便地實現我們程式碼上的優化,減少程式碼體積。

背景

隨著ES6在專案中運用的越來越廣泛,很多人都已經開始使用ES6自帶的模組系統進行程式設計。現在大家寫稍微複雜一點的前端專案的時候,通常都會引入多個模組,如jquery、lodash等等。而這些第三方庫中的很多功能其實可能只會用到一小部分。如果按照傳統的打包方式進行打包的話,你最終的程式碼中將會有很多的冗餘程式碼,有損前端效能。於是,Tree Shaking應運而生。

技術實現

Tree Shaking,從這個技術的命名十分形象,直譯過來就是“搖樹”的意思。在去除程式碼冗餘的過程中,程式會從入口檔案出發掃描所有的模組依賴,以及模組的子依賴,然後將它們連結起來形成一個“抽象語法樹”(AST)。隨後,執行所有程式碼,檢視哪些程式碼是用到過的,做好標記。最後,再將“抽象語法樹”中沒有用到的程式碼“搖落”。這樣一個過程後,就去除了沒有用到的程式碼。

淺談效能優化之Tree Shaking
Tree Shaking

使用Rollup

提到Tree Shaking, 就不得不談一下Rollup這個庫了。Tree Shaking這個術語其實就是它命名出來的。Rollup號稱是下一代ES6的模組化工具。它支援將用ES6模組化系統寫的程式碼打包成CMD,AMD和IFEE等多種風格的模組,從而讓瀏覽器或node能更好地支援。並且,在打包過程中,會使用Tree Shaking技術分析程式碼依賴,移除冗餘程式碼。接下來我將針對Rollup這個庫,來講解一下在專案實踐當中它的應用。

安裝

你用常見的npm或者目前比較流行的yarn都可以進行安裝:

npm install --save-dev rollup
//or
yarn add rollup --dev複製程式碼

配置檔案

Rollup的配置檔案相對來說是比較簡單的,對於基礎的打包工作,只要指定檔案路口、出口以及打包的格式之後就可以了。

先建立一個名為rollup.config.js的配置檔案:

export default {
  entry: 'app.js',
  dest: 'bundle.min.js',
  //打包格式
  format: 'umd',
  sourceMap: false,
}複製程式碼

然後執行命令:

rollup -c rollup.config.js複製程式碼

最後將會把app.js打包成bundle.min.js的檔案。

Rollup官方提供了多種外掛,可以實現很多個性化的需求。如:我們希望在去除程式碼冗餘後,再使用uglify對程式碼進行壓縮,可以使用rollup-plugin-uglify外掛:

import uglify from 'rollup-plugin-uglify';

export default {
  ....
  plugins: [
    uglify()
  ]
}複製程式碼

在Webpck 中使用Tree Shaking

目前Webpack 2.0也已經開始支援Tree Shaking技術,如果需要在打包釋出前用上Tree Shaking,不必再額外引入Rollup庫了。要在webpack中使用,之前可能還會用到babel-preset-es2015-native-modules模組,但是現在只要在.babelrc檔案中做出如下配置:

presets: [["es2015", { "modules": false }]]複製程式碼

就可以了,是不是很簡單呢?

Rollup vs Webpack

另外,在我的學習過程中,曾經有一個疑問:如今Webpack已經整合了Tree Shaking,那麼是否還有必要去使用Rollup呢?直到我讀到了這篇文章:Webpack and Rollup: the same but different。引用文章中的一句總結:

Use webpack for apps, and Rollup for libraries

Webpack和Rollup的使用場景其實不太一樣。Webpack功能十分強大,主要用於解決開發複雜SPA應用時面臨的許多問題:如程式碼分離(code splitting)、靜態資源引用、模組按需載入等。但對於Rollup來說,它則更加輕量級,主要是用來構建能夠被開發者廣泛使用的第三方JS庫的。

問題

目前針對ES6的Tree Shaking技術,還不是特別成熟。特別是你需要處理的程式碼會產生副作用的時候,會導致Tree Shaking並不能很好地去除冗餘。因此在具體的專案開發過程當中,還是建議提高程式碼編寫的質量,減少冗餘。例如利用一些工具,如eslint,jslint之類的檢查策略,來刪除一些沒有用到的業務程式碼。另一方面,我們在專案中也可以採用一些進行優化過的第三方庫。最後,靜態匯入包的過程,直接匯入子模組,會比匯入整個第三方庫來得更合適,如:

import { map } from 'lodash/fp';
//BAD!
import { map } from 'lodash';複製程式碼

題外話

作為dead code elimination的一種方式,Tree Shaking目前已經整合在很多打包工具中了,大家也不妨一試。

相關文章