react 項目優化之 webpack

同步自個人博客javascript

開門見山,因爲咱們項目的前端代碼只有一個bundle,全部代碼都在一個js文件裏,隨着功能不斷的堆疊,體積已經到沒法忍受的地步了(gzip後即將突破300k),致使首屏的時間不停的漲啊漲,最近一週富裕了一點人力趕忙作一次優化,暫時緩住了勢頭。css

react+webapck的優化文章如今一搜一大把,此次只說說個人技術方案,以及我是如何分析和優化的。前端

技術方案

首先,我先說下,gzipcdn等等前端優化手段咱們都是有的,此次主要是爲了解決bundle過大的問題。java

咱們項目的整體架構很簡單易懂,react全家桶:react+redux,哈哈。共有三個系統,每一個系統有本身單獨的工程。因爲是Hybrid應用,爲了儘量貼合APP內的切頁效果,每一個工程又都是採用的是單頁+多頁的形式。node

因爲系統多+單頁多頁混合,致使咱們的路由比較混亂,原本打算上react-router進行一次大的改版,可是時間不夠充裕,綜合時間,開發成本角度來看,我定了一個簡單並能夠快速實施的方案:react

  1. 抽取出三個工程的公共代碼打包出一個bundle。好比reactreduxredux-thunk以及一些polyfill,由於這些都是長時間不會變更的代碼,因此能夠最大程度的命中緩存,而且每次發佈不須要從新下載這部分代碼,在項目中就用externals的方案去引用這些代碼。
  2. code split,每一個工程按照多頁的路由作代碼切割,每一個多頁路由都有本身的bundle
  3. 異步加載,每一個多頁路由都會對應各自的輔助頁面和組件代碼,都使用異步加載的方式。

如何優化bundle體積

code split+異步加載

這個很簡單,使用webpackrequre.ensure就能夠了webpack

addPage(cb){
    require.ensure([], function () {
            cb({
                Page1: require('../../pages/Page1'),
                Page2: require('../../pages/Page2')            
                })
       });
}複製代碼

這裏Page1Page2的代碼都是異步加載的,具體的部分涉及到路由的設計,咱們目前的方案很是的簡單和隨便,就不細說了。我也看了react-router的解決方案,以爲寫起來複雜了一些,後續可能會在尋找更好的方案或者本身擼一個。git

這裏須要注意,雖然咱們的js代碼作了拆分,css文件仍是但願打包成一個,因此須要給ExtractTextPlugin增長allChunks的配置。github

new options.ExtractTextPlugin('[name]@[contenthash].css', {allChunks: true})複製代碼

externals

我將reactredux等等公用的代碼打包成一個lib.js,而後暴露在全局變量上。每一個工程裏都會先去引用這個lib.js,在webpack的配置裏就只須要配置上externals就能夠了web

其餘插件

咱們還用了一些其餘插件來儘量的優化體積

  • UglifyJsPlugin 很少說了,代碼混淆壓縮
  • DedupePlugin 消除重複引用的模塊,好像webpack2已內置,使用webpack2的能夠忽略
  • OccurrenceOrderPlugin 讓依賴次數多的模塊靠前分到更小的id來達到輸出更多的代碼
  • CommonsChunkPlugin這個咱們沒有用,可是你們能夠去看看,對於多頁應用它能夠幫助你將一些公共代碼打包

如何分析bundle過大的問題

在作代碼拆分的時候我曾遇到過一個問題,如何確認個人代碼已是最優的了,如何確認我沒法繼續優化了?這就須要咱們去查看,這個bundle究竟打包了哪些代碼,是否這些都是咱們須要的。

首先我推薦一個網址,這裏介紹了不少webpack優化的工具。

我本身推薦兩個bundle體積的可視化分析工具

  1. webpack-visualizer
  2. webpack-bundle-analyzer

具體如何使用我就不介紹了,它們的文檔寫的很清楚你們能夠去文檔上看,他們均可以很清楚的看到每一個bundle分別打包了哪些代碼,哪些佔據了最大的體積,也能夠觀察哪些代碼實際上是無用的能夠優化掉的。

這裏我還遇到過一個問題,好比我在detail.chunk.js裏發現引入了一個loading組件,可是我映象裏詳情頁並無引入loading組件呀,這時候就須要去尋找loading是被誰依賴了。以前我都是用webstorm的find usages一點點的去看引用關係,其實能夠用webpack提供的一個官方工具來作這件事。

首先,你須要這麼啓動webpack

webpack --profile --json > stats.json複製代碼

此時會生成一個stats.json文件,以後在官方分析工具裏上傳文件便可對你的bundle進行分析。

這裏我用官方的例子簡單說下

  1. 上傳你的json文件,長傳後會看到這麼一個界面,會簡單描述你的webpack的版本,有多少modules,多少chunks等等

    1

  2. 點擊chunks,能夠看到全部chunks的描述,左邊是chunks的id,而後有namse,有多少modules,大小,引用它的chunks是誰、即parents,假如咱們須要分析id爲1的chunk,只須要點擊左邊的id

    2

  3. 這裏你能夠看到更詳細的信息,這裏最重要的是兩個,reasons是引用這個chunks的模塊,modules是這個chunks所引用的modules

    3

  4. 這裏你發現有一個模塊不是你想要的modules,你只須要點擊這個模塊的id,再去查看reasons就能夠看到這個模塊是被誰引入的

如何優化本地開發體驗和打包速度

webpack吐槽的常態 —— 打包慢,這裏說一下咱們這邊作過的優化。

縮小文件搜索範圍

resolve.modules配置爲node_modules,像使用 impot _ from "lodash"這種時webpack遍歷向上遞歸查到node_modules,但一般只有一個node_modules,爲了減小能夠直接寫明node_modules的地址

loader也能夠設置須要生效的目錄地址
好比babelloader能夠只對src目錄裏的代碼進行編譯,忽略龐大的node_modules

{
    test:/\.js$/,
    loader:'babel-oader',
    include:path.resolve(__dirname,'src')
}複製代碼

使用alias

發佈到npm的庫大多包含兩個目錄,一個是放cmd模塊化的lib目錄,一個是全部文件合併成的dist目錄,多數入口文件是指向lib的。默認狀況下webpack會去讀lib目錄下的入口文件再去遞歸加載其餘以來的文件,這個過程很是耗時,alias可讓webpack直接使用dist目錄的總體文件減小遞歸

使用noParse

有些庫是自成一體,不須要依賴別的庫的,webpack無需解析他們的依賴,能夠配置這些文件脫離Webpack解析。

happyPack

happyPack的文檔也寫的很好,就不復制粘貼了,你們能夠自行去閱讀文檔,簡單地說,它主要是利用多進程+緩存使得build更快,這大幅減小了咱們在編譯機上編譯的時間。

後評估

先說說優化完後的結果,因爲react的體積過大,lib就有60k+,基本已經不能繼續優化了,加上咱們的路由設計的很很差,首屏的bundle依然有70k+,總的來講,首屏從280k下降到140k左右。

可是,根據監控的效果來看,頁面js下載的整體時間和白屏時間都只下降了30%左右,這並不符合個人心理預期,想了想除了http請求變多之外並無別的反作用,後續會繼續深刻的分析一下爲何優化的效果沒達到預期。

相關文章
相關標籤/搜索