Webpack 打包優化之體積篇

發表於2017-08-08

談及如今欣欣向榮的前端圈,不僅有各類框架百花齊放,如VueReactAngular等等,就打包工具而言,發展也是如火如荼,百家爭鳴;從早期的王者Browserify, Grunt,到後來贏得寶座的 Gulp, 以及獨樹一幟的 fis3, 以及下一代打包神器 Rollup ;在 browserify,grunt,gulp,rollup,webpack 可以一窺其中部分對比。在本文要探究的是,當前打包工具絕對霸者 Webpack

webpack

Webpack,當前各大主流框架預設配備的打包方案,對其如何使用,已有較完備中英文文件;並且,各主流框架也有對應 CLI 予以基礎配置,故不作為探討範疇。從產品層來講,如何使得構建的包體積小、執行快,這有必要不斷摸索實踐,提煉升級,使之臻於最佳。本文將從以下些許方面,對 Webpack 打包體積方面,做下優化探討(備註Webpack實踐版本: 3.3.0):

定位 webpack 大的原因

這裡推薦使用 webpack-bundle-analyzer —— Webpack 外掛和 CLI 實用程式,她可以將內容束展示為方便互動的直觀樹狀圖,讓你明白你所構建包中真正引入的內容;我們可以藉助她,發現它大體有哪些模組組成,找到不合時宜的存在,然後優化它。我們可以在 專案的 package.json 檔案中注入如下命令,以方便執行她(npm run analyz),預設會開啟 http://127.0.0.1:8888 作為展示。

“analyz”: “NODE_ENV=production npm_config_report=true npm run build”

93f72404-b338-11e6-92d4-9a365550a701

 

當然,同型別的還有 webpack-chart 以及 webpack-analyse,這兩個站點也是以可視方式呈現構造的元件,可以讓你清楚的看到模組的組成部分;不過稍顯麻煩的是,你需要執行以下命令,生成工具分析所需要的 json 檔案:

引入 DllPlugin 和 DllReferencePlugin

DllPlugin 和 DllReferencePlugin 提供了以大幅度提高構建時間效能的方式拆分軟體包的方法。其中原理是,將特定的第三方NPM包模組提前構建?,然後通過頁面引入。這不僅能夠使得 vendor 檔案可以大幅度減小,同時,也極大的提高了構件速度。鑑於篇幅,具體用法可參見:webpack.dll.conf.js

外部引入模組(CDN)

如今前端開發,自然是使用ES6甚至更高版本,擼將起來才更嗨。但由於瀏覽器相容問題,仍得使用 babel 轉換。而這 babel-polyfill 也得引入以確保相容;還比如專案開發中常用到的 moment, lodash等,都是挺大的存在,如果必須引入的話,即考慮外部引入之,再借助 externals 予以指定, webpack可以處理使之不參與打包,而依舊可以在程式碼中通過CMD、AMD或者window/global全域性的方式訪問。

需要補充的是 externals 中:key 是 require 的包名,value 是全域性的變數。

讓每個第三包“引有所值”

確定引入的必要性

前端發展到如今時期,倘若專案採用了 MVVM模式框架,資料雙向繫結,那麼像 jQuery 這般類庫,不能說沒有絲毫引入的必要,至少可以說確實沒有引入的必要。對此,如果還有些顧慮,完全可以參考下 YOU MIGHT NOT NEED JQUERY;用原生寫幾行程式碼就可以解決的事兒,實在不易引入這麼個龐然大物,平添煩惱。

避免類庫引而不用

倘若這類情況發生,對整個打包體積,不僅大而且虧。專案一旦大了,很難人為保證每個引入的類庫,都被有用到,尤其是二次開發。所以工具的利用十分必要,強烈推薦類如 Eslint 這般工具,並且注入對應規則,對宣告卻未使用的程式碼,給予強制提醒;這不僅可以有效的規避類似情形發生(也適用於普通變數的檢測),而且還能使得團隊程式碼風格,儘可能地保持相似;要知道程式碼足夠遵守規則,也可讓壓縮工具更有效壓縮程式碼,一舉多得,何樂不為?

儘量使用模組化引入

如果說 jQuery 確實沒有引入必要,很多人會同意;但對於 lodash 這類依賴的工具,並不是所有人都會去造一發輪子的。然而全包引入 400kb 的體量,可否有讓你心肝一顫?幸好的是,lodash 提供了模組化的引入方式;可按需引入,快哉快哉:

擅懶如你的優秀程式設計師,是否也發現這樣寫頗為麻煩?那麼恭喜你,這個問題已經被解決;lodash-webpack-pluginbabel-plugin-lodash 的存在(組合使用),即是解決這問題的。它可將全路徑引用的 lodash, 自動轉變為模組化按使用引入(如下例示);並且所需配置也十分簡單,就不在此贅述(溫馨提示:當涉及些特殊方法時,尚需些留意)。

額外補充的是,即便採用如上寫法,還是不夠快捷,每個用到的檔案,都寫一遍 import,實在多有不便。更可取的是,將專案所需的方法,統一引入,按需新增,組建出本地 lodash 類庫,然後 export 給框架層(比如 Vue.prototype),以便全域性使用;詳情可參見:vue-modular-import-lodash

儘可能引入更合適的包

作為前端開發的你,想必知道有 momentjs 的存在(Parse, validate, manipulate, and display dates in javascript.);更多的是,你想必知道它很好用,然而它的體態卻十分豐滿(豐盈),沒念及此,是否有重新造輪子的衝動?SpaceTime: A lightweight way to manipulate, traverse, compare, and format dates and times across planet Earth。 具有與 monent 相似 api 的新類庫,其體積又相對小很多(當然,據觀察其靈活度略遜一籌);date-fns:現代JavaScript日期實用程式庫( Modern JavaScript date utility library ),如 lodash 一樣,可支援模組化;知道這些或者更多的你,會如何選擇?

按需非同步載入模組

關於前端開發優化,重要的一條是,儘可能合併請求及資源,如常用的請求資料合併,壓縮合並 js,構造雪碧圖諸此等等(當然得適當,注意體積,過大不宜);但,同時也當因需制宜,根據需要去非同步載入,避免無端就引入早成的浪費。webpack 也是內建對這方面的支援; 假如,你使用的是 Vue,將一個元件(以及其所有依賴)改為非同步載入,所需要的只是把:

改為如下寫法:

如此分割之時,該元件所依賴的其他元件或其他模組,都會自動被分割進對應的 chunk 裡,實現非同步載入,當然也支援把元件按組分塊,將同組中元件,打包在同個非同步 chunk 中。如此能夠非常有效的抑制 Javascript 包過大,同時也使得資源的利用更加合理化。

生產環境,壓縮混淆並移除console

現代化中等規模以上的開發中,區分開發環境測試環境生產環境,並根據需要予以區別對待,已然成為行業共識;可能的話,還會有預釋出環境。對待生產環境,壓縮混淆可以很有效的減小包的體積;同時,如果能夠移除使用比較頻繁的 console,而不是簡單的替換為空方法,也是精彩的一筆小優化。如果使用 UglifyJsPlugin 外掛來壓縮程式碼,加入如下配置,即可移除掉程式碼中的 console

Webpack3 新功能: Scope Hoisting

截止目前(17-08-06), Webpack 最新版本是 3.4.1;Webpack在 3.0 版本,提供了一個新的功能:Scope Hoisting,又譯作“作用域提升”。只需在配置檔案中新增一個新的外掛,就可以讓 Webpack 打包出來的程式碼檔案更小、執行的更快:

據悉這個 Scope HoistingTree Shaking,最初都是由 Rollup 實現的。在個人中實踐中,這個功能的注入,對打包體積雖有影響,卻不甚明顯,有興趣的盆友可以試下;更對關於此功能訊息,可參見 Webpack 3 的新功能:Scope Hoisting

本文自本月(08)四號開始陸陸續續寫,原本的內容意圖是,涉及 Webpack 打包優化的體積和速度兩個方面;豈料,臨近寫完的時候(06號晚),已記不得多久沒關機的 Mac,竟然被重啟了下;屋漏多半會偏逢連夜雨,那一向會自動同步儲存(30min)的作業部落,竟然沒給同步,WTF!整個週末的敲敲打打,皆付之東流,淚崩淚目?。無奈之下,只得重新寫過,直到夜深,才補齊關於體積優化那部分;畢竟涉及內容較多,乾脆,就分成兩部分來完成?。也在此提醒廣大筆友,及時備份資料並確認,這很重要?。在此也預告下一篇 《Webpack 打包優化之速度篇》,當然,此文也扔在完善中。

深圳.南山 @17-08-04. Last Modify 17-08-07


相關文章