【前端構建】WebPack例項與前端效能優化

啃先生發表於2016-03-03

計劃把微信的文章也搬一份上來。

這篇主要介紹一下我在玩Webpack過程中的心得。通過例項介紹WebPack的安裝,外掛使用及載入策略。感受構建工具給前端優化工作帶來的便利。

壹 | Fisrt

曾幾何時,我們是如上圖的方式引入JS資源的,相信現在很少遇見了。近年來Web前端開發領域朝著規範開發的方向演進。體現在以下兩點:

  1. MVC研發構架。多多益處(邏輯清晰,程式注重資料與表現分離,可讀性強,利於規避和排查問題...)

  2. 構建工具層出不窮。多多益處(提升團隊協作,以及工程運維,避免人工處理瑣碎而重複的工作)

    • 模組化開發
    • 將前端效能優化理論落地,程式碼壓縮,合併,快取控制,提取公共程式碼等
    • 其他的還包括比如你可以用ES 6 或CoffeeScript寫原始碼,然後構建出瀏覽器支援的ES5

所以,前端這麼好玩,如果還有專案沒有前後端分離的話,真的是守舊過頭了。

 

主流構建工具

市面上有許多構建工具,包括Grunt、Gulp、browserify等,這些和WebPack都是打包工具。但WebPack同時也具備以下特點:

  1. 相比Grunt,WebPack除了具備豐富的外掛外,同時帶有一套載入(Loader)系統。使它支援多種規範的載入方式,包括ES6、CommonJS、AMD等方式,這是Grunt、Gulp所不具備的。

  2. 從程式碼混淆的角度來看,WebPack更加的極致

  3. 程式碼分片為處理單元(而不是檔案),使得檔案的分片更為靈活。

P.S.此處只做簡單的比較,不論孰優孰劣。其實工具都能滿足需求,關鍵是看怎麼用,工具的使用背後是對前端效能優化的理解程度。

 

貳 | Second 

WebPack安裝與使用

WebPack執行在 NodeJS之下,並且它及其外掛都是使用NPM(NodeJS的包管理工具)管理。

  1. 安裝Node及NPM。到NodeJS官網安裝包,安裝即可

  2. 全域性安裝WebPack。聯網情況下,執行命令列 $npm install webpack -g 即可。 

此至即可使用WebPack了,到WebPack官網去按著Get start(http://webpack.github.io/docs/tutorials/getting-started/)的步驟來,感受一個最簡單的構建過程。 

然而要把WebPack用好,只是跑起來是遠遠不夠的。

 

叄 | Third 

WebPack外掛

花較大篇幅介紹外掛的使用,以下通過在一個DemoApp的構建過程中思考的一些問題(這些問題基本會在每個專案中遇到),讓這些外掛逐一登場。

一、檔案過大

DemoApp最初的構建結果如下:

這裡生成了一個topic.xxx.js,這個檔案本來很小,估計只有10Kb左右,但構建的結果居然快1Mb了。在3G網路(750Kb/s)下的載入瀑布流如下圖:

這張圖(體現前端檔案載入過程)曝露了幾個問題:

  1. 上面箭頭所指的很藍色柱子,說明了大部分時間消耗在載入topic.xxxx.js上。

  2. 所有JS檔案相關的柱子是一根結束了另一根才開始,說明不合理的檔案合併策略,導致檔案序列載入。

  3. 結果就是如底部的箭頭所看到的,頁面的載入時間太長了(3G網路,10+秒)。

觀察構建的檔案,發現原來構建工具把React、jQuery等程式碼庫合併到了topic.xxxx.js,造成此檔案過大。如果再加一個activity模組呢?很明顯,activity.xxx.js得到類似的結果,都太大了,並且React、jQuery等程式碼庫重複被合併到activity和topic裡,如下圖。如果再加模組也會得到同樣的結果,模組越多重複載入的情況越嚴重

可見,提取公共程式碼,情況可以得到改善,另外,壓縮程式碼無疑是可以使檔案變小的。

1. 提取React、jQuery等庫檔案。它們很少變化,並且到處被複用,應該被提取出來,並且得到長時間的快取。

此處使用外掛:WebPack.optimize.CommonsChunkPlugin(WebPack內建外掛)

 

 2. 程式碼壓縮。React由700+ Kb壓縮成100+ Kb

此處使用外掛:WebPack.optimize.UglifyJsPlugin (WebPack內建外掛)

處理後topic.xxx.js和activity.xxx.js都很小了,並且多了jquery.xxx.js和react.xxx.js

 

再看看檔案載入的瀑布流,柱子所佔比例短了,同時資源並行載入。

到此為止,這個問題算比較好地解決了,但還不夠,隨著專案越來越大,還有一個重要因素會導致檔案很大。這部分內容放到本文的最後介紹。

 

二、如何快取

快取控制要做到兩件事情,提到快取命中率

  1. 對於沒有修改的檔案,從快取中獲取檔案

  2. 對於已經修改的檔案,不要從快取中獲取

圍繞這兩點,演繹出了很多方案,此處列兩種:

  • 不處理,等待使用者瀏覽器快取過期,自動更新。這是最偷懶的,命中率低一些,同時可能會出現部分檔案沒有更新,導致報錯的情況。

  • Http頭對檔案設定很大的max-age,例如1年。同時,給每個檔案命名上帶上該檔案的版本號,例如把檔案的hash值做為版本號,topic. ef8bed6c.js。即是讓檔案很長時間不過期。

      • 當檔案沒有更新時,使用快取的檔案自然不會出錯;

      • 當檔案已經有更新時,其hash值必然改變,此時檔名變了,自然不存在此檔案的快取,於是瀏覽器會去載入最新的檔案。

從上面的截圖可以看出來,通過WebPack是可以很輕鬆做到第二點的——只需要給檔名配置上[chunkhash:8]即可,其中8是指hash長度為8,預設是16。

P.S.這樣的處理效果已經很好了,但同樣有劣處,即瀏覽器給這種快取方式的快取容量太少了,只有12Mb,且不分Host。所以更極致的做法是以檔名為Key,檔案內容為value,快取在localStorage裡,命中則從快取中取,不命中則去伺服器取,雖然快取容量也只有5Mb,但是每個Host是獨享這5Mb的。

 

三、自動生成頁面

檔名帶上版本號後,每一次檔案變化,都需要Html檔案裡手動修改引用的檔名,這種重複工作很瑣碎且容易出錯。

使用 HtmlWebpackPlugin 和 ExtractTextPlugin 外掛可以解決此問題。

  • 生成帶JS的頁面

  • 生成帶css的頁面

  new ExtractTextPlugin("comm.[contenthash:9].css")

  外掛介紹到此為止,然而,還有一個關於同步載入和非同步載入的問題,否則入口檔案還是會很臃腫。

 

肆 | Fourth

關於同步載入和非同步載入

使用WebPack打包,最爽的事情莫過於可以像伺服器程式設計那樣直接require檔案,看起來是同步地從伺服器上取得檔案直接就使用了。如下面的程式碼一樣,沒有任何非同步邏輯,程式碼很乾淨。

 

然而,這種爽是有代價的,對於直接require模組,WebPack的做法是把依賴的檔案都打包在一起,造成檔案很臃腫。

所以在寫程式碼的時候要注意適度同步載入,同步的程式碼會被合成並且打包在一起;非同步載入的程式碼會被分片成一個個chunk,在需要該模組時再載入,即按需載入,這個度是要開發者自己把握的,同步載入過多程式碼會造成檔案過大影響載入速度,非同步過多則檔案太碎,造成過多的Http請求,同樣影響載入速度。

  • 同步載入的寫法,如:

     var TopicItem = require('../topic/topicitem');

  • 非同步載入的寫法,如:

一個原則是:首屏需要的同步載入,首屏過後才需要的則按需載入(非同步)

 

結語

以上是WebPack構建工具比較好的實踐,可見,要用好還是很考驗前端效能優化的功力的,比較什麼時候同步,什麼時候非同步,如果做快取等等。

如果覺得文章有用,順手點選下方的推薦 

 

相關文章