react項目優化之webpack

PNYLilia 7年前發布 | 28K 次閱讀 React webpack

開門見山,由于我們項目的前端只有一個 bundle ,所有代碼都在一個js文件里,隨著功能不斷的堆疊,體積已經到無法忍受的地步了(gzip后即將突破300k),導致首屏的時間不停的漲啊漲,最近一周富裕了一點人力趕緊做一次優化,暫時緩住了勢頭。

react+webapck的優化文章現在一搜一大把,這次只說說我的技術方案,以及我是如何分析和優化的。

技術方案

首先,我先說下, gzip , cdn 等等前端優化手段我們都是有的,這次主要是為了解決 bundle 過大的問題。

我們項目的總體架構很簡單易懂, react 全家桶: react + redux ,哈哈。共有三個系統,每個系統有自己單獨的工程。由于是 Hybrid 應用,為了盡可能貼合 APP 內的切頁效果,每個工程又都是采用的是單頁+多頁的形式。

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

  1. 抽取出三個工程的公共代碼打包出一個 bundle 。比如 react , redux , redux-thunk 以及一些 polyfill ,因為這些都是長時間不會變動的代碼,所以可以最大程度的命中緩存,并且每次發布不需要重新下載這部分代碼,在項目中就用 externals 的方案去引用這些代碼。
  2. code split ,每個工程按照多頁的路由做代碼切割,每個多頁路由都有自己的 bundle 。
  3. 異步加載,每個多頁路由都會對應各自的輔助頁面和組件代碼,都使用異步加載的方式。

如何優化bundle體積

code split+異步加載

這個很簡單,使用 webpack 的 requre.ensure 就可以了

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

這里 Page1 和 Page2 的代碼都是異步加載的,具體的部分涉及到路由的設計,我們目前的方案非常的簡單和隨便,就不細說了。我也看了 react-router 的解決方案,覺得寫起來復雜了一些,后續可能會在尋找更好的方案或者自己擼一個。

這里需要注意,雖然我們的 js 代碼做了拆分, css 文件還是希望打包成一個,所以需要給 ExtractTextPlugin 增加 allChunks 的配置。

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

externals

我將 react , redux 等等公用的代碼打包成一個 lib.js ,然后暴露在全局變量上。每個工程里都會先去引用這個 lib.js ,在 webpack 的配置里就只需要配置上 externals 就可以了

其他插件

我們還用了一些其他插件來盡可能的優化體積

  • 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 等等

  2. 點擊 chunks ,可以看到所有 chunks 的描述,左邊是 chunks 的id,然后有 namse ,有多少 modules ,大小,引用它的 chunks 是誰、即 parents ,假如我們需要分析 id 為1的 chunk ,只需要點擊左邊的 id

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

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

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

webpack吐槽的常態 —— 打包慢,這里說一下我們這邊做過的優化。

縮小文件搜索范圍

將 resolve.modules 配置為 node_modules ,像使用 impot _ from "lodash" 這種時webpack遍歷向上遞歸查到 node_modules ,但通常只有一個 node_modules ,為了減少可以直接寫明 node_modules 的地址

loader 也可以設置需要生效的目錄地址

比如 babel 的 loader 可以只對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請求變多以外并沒有別的副作用,后續會繼續深入的分析一下為什么優化的效果沒達到預期。

 

來自:http://zhangfe.github.io/2017/03/12/webpack優化三兩事/

 

 本文由用戶 PNYLilia 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!