就這幾個小操做,我把項目性能優化了幾十倍!

前言

說到性能優化,你們確定能想到不少內容。好比說如何修改 Webpack 配置來達到構建提速以及優化產物的需求;好比說如何對頁面進行性能優化等等。前端

其實性能優化還存在不少能夠玩的地方,今天筆者就來聊一些你們不常關注的地方,從開發到 CI 的構建階段以及最後部署上線這幾個鏈路。node

構建階段

構建階段分爲兩個部分:react

  • 本地開發
  • CI 構建

這兩部分各自側重的點並不相同。前者更加關注構建速度,對於產物沒有什麼要求,畢竟本地開發時候構建是一件很頻繁的事情,速度慢就意味着下降開發效率;後者更關注產物,好比說文件體積、數量等等指標,固然了構建速度也不能說徹底不關心,但可能是在保證產物的前提下再去提高構建速度。webpack

接下來筆者會就這兩部分來分別聊聊咱們能夠作的性能優化。git

本地開發

以 Webpack 爲例,本地開發構建這部分是咱們平常工做中最常接觸到的,若是能帶來必定的提速,那麼感知仍是挺強的。github

這部分主要分爲兩個階段:web

  • 啓動項目,好比說 yarn start
  • 修改保存代碼觸發熱更新

第一階段相對來講頻率低點,速度慢點在大部分狀況下仍是能夠忍受的。面試

可是第二階段是咱們前端開發天天都會進行數十次甚至上百次的高配操做,而且對於一些中大型項目來講,在這部分花的時間一次可能就須要 10 秒多。假設老王天天修改 100 次代碼,那麼在這部分天天都得等待上 15 分鐘甚至更多。shell

那麼咱們有辦法去提效這部份內容麼?答案是有的,應該不少讀者都早已瞭解到這類產品了:Vitenpm

Vite 屬於 NoBundle 方案,也能夠稱之爲 UnBundle、Bundleless,反正都是同一個玩意。同類競品還有 snowpackwmr 等,固然名氣都不如前者大。

若是你已經使用過 Vite,那麼應該對它秒級啓動以及熱更新有了映像,相比單純的 Webpack 提高巨大。

截屏2021-06-06下午10.05.06

上圖出自該文章,項目有 15 萬行代碼,很大型的一個項目了,你們能夠發如今啓動以及熱更新上真的是有數十倍的提高,這對於開發體驗來講真的流暢不少。另外筆者以前也參與過公司內部 NoBundle 方案的研發,得出的數據也是很可觀的。

那麼爲何 Vite 能帶來這樣的體驗的呢,究竟是如何實現這樣的效果的?筆者這裏就粗略的來聊聊。

首先來聊聊 Webpack 構建,基於 Webpack4 以及中大型項目來講。當咱們執行 yarn start 之後,Webpack 會開始全量打包,構建出依賴圖後把全部內容都打成幾個文件,這個構建依賴圖以及打包的過程會花掉咱們數分鐘。

當觸發熱更新的時候,Webpack 也須要找出這一條依賴鏈路並再次對鏈路進行打包,所以這個過程耗時也很多。固然若是你用上了 Webpack5 的話,基於持久化緩存應該能提速很多。

基於打包器的開發服務器

可是對於 Vite 來講就不須要這樣幹了。

首先 Vite 使用到了 ESBuild 來預構建依賴,這是一個用 Go 寫的特別牛逼的構建器,效率相比 JS 來講快了數十倍甚至百倍,Vite 須要利用這個來處理模塊以及構建 ESM 環境。

另外就是得益於 ESM 按需加載的特性,咱們無需啓動項目的時候構建依賴圖以及打包文件,而是瀏覽器請求什麼文件才編譯什麼文件(好比說編譯 TS、插入熱更新代碼等),依託於這個特性咱們能很快的跑起來項目。

最後當用戶觸發熱更新的時候,Vite 也無需像 Webpack 那樣作,而是找出最小的依賴路徑(通常來講就是修改的那個文件),而後修改下文件的 hash 下發給瀏覽器失效以前的緩存便可。

基於上面的一些特性以及 ESBuild,Vite 基本不會由於代碼量變大而形成速度有明顯拖慢,可是對於 Webpack 來講,項目體積很明顯會拖慢構建速度。

基於 ESM 的開發服務器

讀者看到這裏,可能以爲這玩意確實牛逼,摩拳擦掌準備幹上一番。可是,這裏要給各位潑個冷水。根據咱們內部的 Nobundle 使用結果以及筆者與多位在大廠作過這個方案的朋友交流的結果來看,接入業務成本巨大,雖然提效不錯,可是以接入的成原本看可能投入產出比就不是那麼可觀了。目前也沒有一套很好的接入方案,極可能在不一樣的項目裏會踩到不一樣的坑,最大的緣由仍是來自於 ESM 環境。

可是筆者認爲 Nobundle 之後必定會成爲本地開發構建的主流,由於開發體驗實在太順滑了。

另外極可能讀者會問 Vite 是否會取締 Webpack 這類問題。筆者認爲這兩個東西算不上是競品,Webpack 能適用於複雜、須要定製化的場景,這點 Vite 是作不到的(將來可能也不會去作),Vite 主要是爲了改善開發階段的體驗,頂多在開發階段 Vite 能頂替掉 Webpack 的工做。

如下筆者列了點資料,你們有興趣能夠了解下:

最後你們若是有興趣在業務裏作遷移的話,必定要多看看市面上遷移相關的文章,能幫助你們在踩坑的時候快速解決問題。

CI

在 CI 中構建大體分爲三個環節:

  1. 安裝依賴
  2. 代碼質量保障
  3. 構建

前兩個環節涉及到的內容很少,筆者這裏就快速帶過。

安裝依賴

依賴安裝仍是挺耗時的,咱們能夠經過如下幾點來加速這個環節:

  • 源必須切換到淘寶源或者本身的私有源
  • 緩存 node_modules 必須整上
  • 有條件能夠試試 yarn2

這裏稍微聊一下 yarn2 這個事情。升級到 yarn2 之後會有兩種可選方案,一種仍是 node_modules 的方案,另外一種是拋棄 node_modules,轉而使用 PnP。

這兩種方案前者在遷移的過程當中基本不用動代碼,可是已經能改善依賴安裝的速度;後者須要變動的地方還挺多,在內部所有推廣開來仍是存在一部分阻力的,可是這種方案可以大幅度減小依賴體積以及改善安裝速度,你們能夠自行評估投入產出比。

代碼質量保障

代碼質量保障通常分爲兩塊:

  • ESLint
  • 單測

固然還有別的質量保障方案,這裏就不表了。

ESLint 其實沒啥優化的方案,卻是本地提交代碼的時候大部分項目應該都作了優化,只是可能不少人都忽略了。想必你們項目中應該都會存在 husky + Lint-stage,這兩個工具其實能幫助咱們在提交代碼的時候只對須要提交的文件進行 lint。這是一種增量的思路,在不少狀況下咱們都須要這種思路來幫助咱們作性能優化。

對於單測來講,可能不少讀者壓根就沒寫過這玩意。可是若是你作過一些 npm 包或者 Node 服務的話,會發現單測仍是挺有必要的。

對於大型的項目來講,Test case 是至關多的。以咱們內部的組件庫爲例,總共有 1000+ 的 Test case,光在本地完整執行一次 yarn test 就足足須要花費兩三分鐘,在雲端跑的速度就更不用說了。可是實際上咱們每次提交的代碼影響到的 Test case 遠沒有那麼多,每次全量跑單測花費的時間真的太多了。

說到這兒,讀者們應該能記起筆者上文提到過的增量。沒錯,在這裏咱們徹底可使用增量來提升跑單測的速度。若是你使用 Jest 框架的話,能夠了解下和 —onlyChanged 相關聯的參數來實現增量單測。

構建

說到構建優化,想必不少讀者都會說這題我會。畢竟優化 Webpack 配置已經算是面試考爛的題目了,而且市面上關於這類的文章也是層出不窮。

所以筆者這裏就再也不來聊咱們該這樣那樣配置 Webpack,若是讀者有須要的話能夠自行網上翻閱資料。

其實除了修改 Webpack 來達成性能優化的目的,升級版本也會有很大的驚喜。

好比說從 4 升級到 5 之後,咱們能夠經過這些新增特性來實現提效:

  • 持久化緩存,這玩意上文已經講過了,能夠幫助咱們提升二次啓動及 HMR 的速度
  • 更好用的 Tree shaking 能力,可以更好地清除未使用的導出,進一步下降構建產物的體積
  • Prepack 能力,經過靜態計算下降代碼數量
  • 聯邦模塊,可以運行時加載遠程模塊或者依賴,減小構建所帶來的時間消耗

筆者以上列舉了一部分升級 Webpack 能帶來的收益,你們若是對某個特性有興趣的話能夠自行搜索文章。

另外其實咱們常說的構建速度優化,其中有一個點關注的人並很少,可是對構建速度也有不小的影響,那就是壓縮代碼。

若是你用過 Speed Measure Plugin 這個插件的話,就能發現筆者所言不虛。對於大型應用來講,就算你使用多線程進行壓縮,最終可能仍是會花費二三十秒的時間。

固然了,咱們是可以對這個階段作優化的,用到的工具上文也說過,也就是 ESBuild。ESBuild 主打的就是構建快,從官方的性能對比圖裏能夠看出是降維打擊其它構建器。

截屏2021-06-08下午10.34.37

恐怖的數百倍提高(固然筆者實測拿不到這樣的數據),但即便它構建速度確實很快,目前仍是存在了一些問題(最大的問題是 CSS 上的處理)致使上生產仍是不大現實。可是實際上 ESBuild 還支持用於壓縮代碼,風險基本能夠忽略,筆者實測業務項目中能帶來 30% ~ 40% 的速度提高,仍是至關可觀的。

另外除了以上說的這些以外,其實在構建這個環節中咱們也能夠經過增量的思路來提高效率。

對於多頁應用來講,大部分狀況下咱們每次發佈所修改的代碼不會影響到全部的入口。所以沒有被影響到的入口實際是不須要再次被構建的,直接使用以前的緩存就好了。那麼根據這個思路,咱們須要每次在構建前找出上次發佈到當前爲止全部變更過的文件以及這些文件所影響的入口,最後動態修改 Webpack 的入口配置便可實現增量構建

說幹就幹,如下是增量構建的大體思路。

首先是找到距離上次發佈後有變動的文件,這個很簡單,一行命令就搞定了:

git diff --name-only {git tag / commit sha}
複製代碼

部署完別忘了打個 tag 或者記錄一下 commit id,下次執行命令的時候傳入。

當咱們拿到變動後的文件名後,接下來須要找出這些文件所影響的入口,所以須要開始構建依賴樹。雖然 Webpack 也會幫助咱們構建這個,可是咱們不必用到那麼重的東西,找個專一依賴樹的庫就行,你們能夠選擇 madge 或者別的相似產品。

接下來咱們只須要匹配文件找到影響的幾個入口就行,而後動態修改 Webpack 配置裏的 entry 屬性進行構建便可。

最後咱們將構建的內容替換掉以前的舊入口產物就好了,沒有變更的不須要管。

其實這個多頁應用的增量構建作法和 monorepo 裏的部署很類似。若是咱們在 monorepo 裏只須要對改過代碼的 package 進行部署的話,那麼部署代碼的邏輯是很類似的,一樣也是找到被影響的 package(多頁應用裏就是入口了),而後進行構建發佈。

若是你們業務中也存在多頁應用的項目,那麼能夠嘗試下該方案,帶來的收益應該會很可觀。

小結

說了那麼多,筆者來總結一下上文中聊過的優化手段:

  • 開發階段嘗試使用 NoBundle 替換 Webpack,效果很好,可是遷移成本須要考量
  • ESBuild 是個好東西,既能用於構建,又能用於壓縮代碼。前者存在風險且存在處理不佳的場景,後者風險很小,效率也能有不錯的提升
  • 安裝依賴提速能夠從源、緩存、升級 yarn2 上着手
  • 大型項目代碼質量保障階段耗時過長,考慮經過增量方案來提速,固然若是你以爲全量跑一遍更安心也沒啥毛病
  • CI 構建層面,Webpack 配置相關的說爛了,還不瞭解的能夠自行了解,另外升級 Webpack 也會有意想不到的收益,固然遷移成本仍是有的
  • 多頁應用不必每次都把全部入口構建一遍,只構建代碼影響的入口便可
  • 增量思路在性能優化裏至關廣泛

上線後

上線後的通用性能優化也被說爛了,無非從網絡協議、CSS、Webpack 配置入手,筆者仍是來說點別的。

既然要聊性能優化,那麼咱們確定得知道到底哪裏存在性能問題,不然就是虛空優化了。如何檢測性能優化、到底有哪些性能指標也是筆者常問的面試問題(固然得面試者簡歷裏寫了作過這方面),可是大部分時候獲得的答案在筆者看來是不正確的,並不能肯定到底對方是否是真的作過這方面的優化。

好比說談到性能指標,問十人九人必會說白屏時間,可是其實白屏時間在當下並非一個合格的指標。大部分應用開屏都會存在 Loading 或者骨架屏,在這些內容過渡到頁面出現用戶關心的內容還須要一段時間。可是若是咱們僅僅靠收集白屏時間來判斷用戶看到 DOM 出現是錯誤的作法,單靠這個指標去作開屏的優化是遠遠不夠的,咱們必須得收集到用戶看到真實 DOM 的時間。

此時咱們能夠收集 LCP(Largest Contentful Paint)指標,這個指標會幫助咱們記錄頁面中最大內容繪製的時間戳。

img

經過這個指標外加白屏時間,咱們纔可以正確的去作開屏時間的優化。另外在這裏不使用 LCP 指標也是能夠的,咱們能夠本身給關鍵 DOM 打點,實現個性化的收集。

除了 LCP 指標以外,還存在很多新的指標,你們有興趣的能夠了解下筆者以前寫的文章,文中作幾個新的指標作了闡述並說明了該如何優化這些指標。

用戶體驗指標

最後

以上就是本文的所有內容了。性能優化是一個很大的話題,除了那些耳熟能詳的手段以外,其實還存在着很多方案能作。

你們若是有什麼疑問歡迎在評論區交流。

相關文章
相關標籤/搜索