前端高級進階N: 在前端中如何更好地優化打包資源

這是山月關於高級前端進階暨前端工程系列文章的第 M 篇文章 (M 隨便打的,畢竟也不知道能寫多少篇),關於前 M-1 篇文章,能夠從個人 github repo shfshanyue/blog 中找到,若是點進去的話能夠捎帶~點個贊~,若是沒有點進去的話,那就給這篇文章點個贊。今天的文章開始了javascript

本篇文章地址在 前端工程化系列,歡迎訂閱。css


在前端中但凡談到打包,確定要說起到 webpack,畢竟它如今已是最爲流行的打包工具。但 webpack 更多地是表如今 上,因而我決定寫這篇文章,更多地講解一些關於 的。html

對於一個前端而言,生產環境的靜態資源優化,它既是面試中的高頻問題,同時也最容易成爲平時工做中的 OKR/KPI。若是你常常致力於優化前端打包說起,必然會對一些數字極爲敏感,好比:前端

  1. lodashreact gzip 後的體積是多少 (定性,能夠給出範圍)
  2. 打包 moment 時會有什麼問題
  3. 大家線上前端項目首屏靜態資源 gzip 後的體積是多少

若是你負責了大家前端項目的打包優化,若是以上問題連一個都不瞭解那麼是說不通的。以我做爲面試官的兩年經驗中,若是候選人對這些問題有所瞭解的話,每每對打包以及webpack的瞭解就會相對深刻一些java

原則

通常談到打包會有兩方面的意思,第一在於提升打包的速度,第二在於對打包後的靜態資源的優化。而對於靜態資源的優化又不只僅是打包說起的縮減。node

對於打包資源優化的整體原則,在於儘量的減小或者延遲模塊的引用。主要遵循如下三點react

  1. 減少打包的總體體積
  2. Code Splitting: 按需加載,優化頁面首次加載體積。如根據路由按需加載,根據是否可見按需加載
  3. Bundle Splitting:分包,根據模塊更改頻率分層次打包,充分利用緩存

接下來本篇文章將會結合實例分別闡述這三點webpack

01 減少打包的總體體積

第一種方法是減少打包的總體體積。減少打包的整體積有多種方式,這每每也是打包資源優化的着力點,一方面操做性高易於實踐,~另外一方面有具體數據支撐易於寫PPT來晉升~。我從網站性能優化的實踐角度,來分爲如下幾個方面git

代碼壓縮

代碼壓縮能夠很是可觀地減少資源打包體積,可是它的可操做性空間太小。可操做性低的意思是這一項不太容易出如今晉級評審的PPT上,如同 CDN 在網站性能優化的重要程度同樣,重要但不歸你作(或者傻瓜式配置)。github

它良好的模塊化,以至於 webpack 就自做主張在生產環境中默認把這件事給作了。

那它是如何壓縮代碼的?最典型的兩種方法就是空白符替換以及縮短變量名,如代碼所示,僅僅經過這兩種方式就大大壓縮了 javascript 資源:

// 壓縮前
function sum (first, second) {
  return first + second;  
}

// 壓縮後
function s(x,y){return a+b}

關於代碼壓縮,能夠參考山月的前端高級進階系列javascript 代碼的體積是如何被壓縮的

移除沒必要要的模塊

這句話好像是廢話,但它倒是真正有用而且極爲容易實現的一點。

在如下代碼中,對 lodash 這個模塊進行了引入,但在以後的代碼中並沒有使用 lodash,那在 webpack 中這個模塊還會繼續打包嗎?

很遺憾,仍會對它進行打包。但好消息是這一點優化起來至關簡單。

// 僅僅引入而未在代碼中使用,該模塊仍然會被打包
import _ from 'lodash'

對於這類問題總應該防患於未然,扼殺於搖籃中。eslint 的用武之地來了,它除了統一團隊的代碼風格之外,也用來提升團隊的代碼質量以及性能。

選擇可替代的體積較小的模塊

針對這一條,有一個典型的例子是以體積過大而臭名昭著的 moment.js 模塊,它僅僅用於 DateTime 的格式化及各類計算。但你 import 以後它的體積居然達到了 200kb+,gzip 後仍然有 69kb。以致於在 github 上有一個倉庫專門用來介紹如何優化它,

再來一張圖感覺一下它巨大的體積吧:

此時能夠選擇一個可替代它功能,但體積更小的模塊。與 moment.js API 兼容的 day.js,它 gzip 後體積僅僅只有 2kb。

按需引入模塊

當你面對一個巨無霸的,捆綁式的大型模塊時,可能你並不會使用到它的全部的功能,你只須要按照你的需求引入模塊就能夠了。那常常會有哪些巨無霸模塊呢?

lodash (勉強算),antdecharts,我相信這三個模塊對於以 React 爲主的前端工程師都或多或少使用過。對你所須要使用的模塊單獨引入:

import DatePicker from 'antd/es/date-picker'; // for js
import 'antd/es/date-picker/style/css'; // for css

import get from 'lodash.get'

02 Code Splitting: 按需加載,優化頁面首次加載體積

懶加載,若是面試中提到懶加載的話,大機率面試官此時是想問你關於圖片懶加載的問題。

前端開發中的圖片懶加載如何實現

經過 Code Splitting 能夠只加載當前所須要的核心資源:

  1. 若是你處在首頁,而且首頁中有佔用資源太重的圖表,須要對圖表懶加載,不然它會大幅拖垮應用的首次渲染,加大白屏時間
  2. 若是你處在首頁,你無需加載當前不可見屏幕下方的複雜組件
  3. 若是你處在頁面 A,你沒有必要加載頁面 B 的資源

他們實現起來均須要額外編寫代碼,因此可操做性中等,可是好在它可以帶來極大的益處,投資回報率較高,操做起來也極爲簡單,接下來就屬於體力活了:

  • 使用 import() 動態加載模塊
  • 使用 React.lazy() 動態加載組件
  • 使用 lodable-component 動態加載路由,組件或者模塊

大部分狀況下,你只要作一個莫得感情的 API 工程師調用以上三個 API 就能夠解決問題,大幅度下降頁面的首次加載體積。可是在前往高級前端工程師的路上,你有可能須要瞭解其中的原理,(有可能並不須要,數據比原理重要) 來作更加精細化的控制,好比針對緩存。

Code Splitting 的原理是什麼?

03 Bundle Splitting

除了資源體積上的優化,另外一個大的優化就是緩存。單頁應用有一個最好的方面,就是全部資源都是帶有指紋信息的,這意味着全部的資源都是可以設置永久緩存的。

但僅僅如此了嗎?

若是你全部的 js 資源都打包成一個文件,它確實有永久緩存的優點。可是當有一行文件進行修改時,這一個大包的指紋信息發生改變,永久緩存失效。

因此咱們如今須要作到的是:當修改文件後,形成最小範圍的緩存失效,這樣便可以更充分的利用緩存,減少寬帶,減少服務器費用。一個好消息是 webpack 等打包工具雖然在 optimization 上內置了不少性能優化,但它不會幫你作這件事,它並不知道你有哪些模塊,以及這些模塊的重要緊急程度,你終於能夠大展拳腳了。

此時咱們能夠對資源進行分層次緩存的打包方案,這是一個建議方案

  1. webpack-runtime: 應用中的 webpack 的版本比較穩定,分離出來,保證長久的永久緩存
  2. react-runtime: react 的版本更新頻次也較低
  3. vundor: 經常使用的第三方模塊打包在一塊兒,如 lodashclassnames 基本上每一個頁面都會引用到,可是它們的更新頻率會更高一些

隨着 http2 的發展,特別是多路複用,初始頁面的靜態資源不受資源數量的影響。所以爲了更好的緩存效果以及按需加載,也有不少方案建議把全部的第三方模塊進行單模塊打包。

在 webpack 中,使用 splitChunks.cacheGroups

{
  splitChunks: {
    cacheGroups: {
      react: {
        test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
        name: 'react',
        chunks: 'all'
      },
      vendor: {

      }
    }
  },
  runtimeChunk: {
    name: entrypoint => `runtime-${entrypoint.name}`,
  },
}

小結

毫無疑問在前端中更好地優化打包資源屬於網站性能優化強操做性部分的重中之重,整理下本篇文章關於資源優化的全部內

  1. 減少打包的總體體積

    • 代碼壓縮
    • 移除沒必要要的模塊
    • 按需引入模塊
    • 選擇能夠替代的體積較小的模塊
  2. Code Splitting: 按需加載,優化頁面首次加載體積。如根據路由按需加載,根據是否可見按需加載

    1. 使用 import() 動態加載模塊
    2. 使用 React.lazy() 動態加載組件
    3. 使用 lodable-component 動態加載路由,組件或者模塊
  3. Bundle Splitting:分包,根據模塊更改頻率分層次打包,充分利用緩存

與我交流

掃碼添加個人機器人微信,將會自動(自動拉人程序正在研發中)把你拉入前端高級進階學習羣

我是山月,能夠加我微信 shanyue94 與我交流,備註交流。另外能夠關注個人公衆號【全棧成長之路】

若是你對全棧面試,前端工程化,graphql,devops,我的服務器運維以及微服務感興趣的話,能夠關注我

相關文章
相關標籤/搜索