移除用不到的程式碼

在本程式碼研究室中,您將移除所有未使用且不必要的依附元件,以提升下列應用程式的效能。

應用程式螢幕截圖

測量

建議您先評估網站的效能,再進行最佳化。

  • 如要預覽網站,請按下「View App」。然後按下「Fullscreen」圖示 全螢幕

請按一下你最喜歡的小貓咪!這個應用程式會使用 Firebase 的即時資料庫,因此分數會即時更新,並與使用該應用程式的其他使用者同步。🐈?

  1. 按下 `Control+Shift+J` 鍵 (在 Mac 上為 `Command+Option+J` 鍵) 開啟開發人員工具。
  2. 按一下 [網路] 分頁標籤。
  3. 勾選「Disable cache」核取方塊。
  4. 重新載入應用程式。

原始組合大小為 992 KB

系統會傳送近 1 MB 的 JavaScript 來載入這個簡單的應用程式!

查看開發人員工具中的專案警告。

  • 按一下「控制台」分頁標籤。
  • 請確認 Filter 輸入框旁的層級下拉式選單中已啟用 Warnings

警告篩選器

  • 查看顯示的警告。

主控台警告

Firebase 是這個應用程式中使用的其中一個程式庫,它會提供警告,讓開發人員知道不要匯入整個套件,只匯入所用元件。換句話說,這個應用程式中有未使用的程式庫,可以移除這些程式庫來加快載入速度。

在某些情況下,您可能會使用特定程式庫,但可能有更簡單的替代方案。稍後本教學課程會探討移除不需要的程式庫的概念。

分析套件

應用程式中有兩個主要依附元件:

  • Firebase:這個平台可為 iOS、Android 或網路應用程式提供多項實用服務。這裡的 即時資料庫會用於即時儲存及同步處理每隻小貓的資訊。
  • Moment.js:實用程式庫,可讓您更輕鬆地在 JavaScript 中處理日期。每隻小貓的出生日期都會儲存在 Firebase 資料庫中,而 moment 則用於計算小貓的年齡 (以週為單位)。

為什麼只有兩個依附元件,套件大小就會增加到將近 1 MB?其中一個原因是任何依附元件都可能會反過來擁有自己的依附元件,因此如果考量依附元件「樹狀圖」的每個深度/分支,就會發現有更多層級。如果應用程式包含許多依附元件,很容易在短時間內變得龐大。

請分析 Bundler,進一步瞭解發生了什麼事。社群成員開發了許多不同的工具,可協助您完成這項作業,例如 webpack-bundle-analyzer

這個工具的套件已以 devDependency 的形式納入應用程式。

"devDependencies": {
  //...
  "webpack-bundle-analyzer": "^2.13.1"
},

也就是說,您可以直接在 webpack 設定檔中使用此類別。在 webpack.config.js 的開頭處匯入:

const path = require("path");

//...
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
  .BundleAnalyzerPlugin;

接著,在 plugins 陣列中,於檔案的最後方新增外掛程式:

module.exports = {
  //...
  plugins: [
    //...
    new BundleAnalyzerPlugin()
  ]
};

應用程式重新載入時,您應該會看到整個套件的視覺化呈現,而不是應用程式本身。

Webpack Bundle Analyzer

雖然不如看到小貓咪那麼可愛 🐱?,但還是非常實用。將滑鼠游標懸停在任何套件上,即可透過三種方式查看其大小:

統計資料大小 經過任何精簡或壓縮處理前的大小。
剖析大小 已編譯的套件中實際套件的大小。這個應用程式使用的 webpack 4 會自動縮減編譯檔案,因此這個值會小於統計資料大小。
已壓縮的大小 套件使用 gzip 編碼壓縮後的大小。這個主題會在另一份指南中說明。

使用 webpack-bundle-analyzer 工具,您可以更輕鬆地找出佔用軟體包大部分空間的未使用或不必要的軟體包。

移除未使用的套件

視覺化資訊顯示 firebase 套件包含的內容不只資料庫。其中包含額外套件,例如:

  • firestore
  • auth
  • storage
  • messaging
  • functions

這些都是 Firebase 提供的絕佳服務 (請參閱說明文件瞭解詳情),但應用程式並未使用任何一項服務,因此沒有理由要全部匯入。

如要再次查看應用程式,請還原 webpack.config.js 中的變更:

  • 從外掛程式清單中移除 BundleAnalyzerPlugin
plugins: [
  //...
  new BundleAnalyzerPlugin()
];
  • 接著,從檔案頂端移除未使用的匯入項目:
const path = require("path");

//...
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

應用程式現在應可正常載入。修改 src/index.js 以更新 Firebase 匯入內容。

import firebase from 'firebase';
import firebase from 'firebase/app';
import 'firebase/database';

現在,當應用程式重新載入時,開發人員工具警告就不會顯示。開啟 DevTools 的「Network」面板,也會顯示組合大小的不錯縮減幅度:

套件大小縮減至 480 KB

移除超過一半的套件大小。Firebase 提供多種不同服務,開發人員可以選擇只納入實際需要的服務。在這個應用程式中,只有 firebase/database 用於儲存及同步處理所有資料。您必須匯入 firebase/app,為每項不同的服務設定 API 介面。

許多其他熱門程式庫 (例如 lodash) 也允許開發人員選擇性匯入套件的不同部分。只要稍微調整應用程式中的程式庫匯入作業,只保留目前使用的程式庫,就能大幅改善效能。

雖然套件大小已大幅縮減,但仍有改進空間!😈

移除不需要的套件

與 Firebase 不同,匯入 moment 程式庫的部分內容無法輕易完成,但或許可以完全移除?

每隻可愛小貓咪的生日會以 Unix 格式 (毫秒) 儲存在 Firebase 資料庫中。

以 Unix 格式儲存的出生日期

這是特定日期和時間的時間戳記,以自世界標準時間 1970 年 1 月 1 日 00:00 起經過的毫秒數表示。如果可以使用相同格式計算目前的日期和時間,您可能可以建立一個小函式,以週為單位找出每隻小貓的年齡。

如往常一樣,請勿在操作時複製貼上內容。首先,請從 src/index.js 的匯入項目中移除 moment

import firebase from 'firebase/app';
import 'firebase/database';
import * as moment from 'moment';

我們有一個 Firebase 事件監聽器,可處理資料庫中的值變更:

favoritesRef.on("value", (snapshot) => { ... })

在上述程式碼上方,新增一個小函式,用於計算指定日期的週數:

const ageInWeeks = birthDate => {
  const WEEK_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 7;
  const diff = Math.abs((new Date).getTime() - birthDate);
  return Math.floor(diff / WEEK_IN_MILLISECONDS);
}

在這個函式中,系統會計算目前日期和時間 (new Date).getTime() 與出生日期 (birthDate 引數,已以毫秒為單位) 之間的差距 (以毫秒為單位),然後除以一週的毫秒數。

最後,您可以改用這個函式,在事件監聽器中移除所有 moment 例項:

favoritesRef.on("value", (snapshot) => {
  const { kitties, favorites, names, birthDates } = snapshot.val();
  favoritesScores = favorites;

  kittiesList.innerHTML = kitties.map((kittiePic, index) => {
    const birthday = moment(birthDates[index]);

    return `
      <li>
        <img src=${kittiePic} onclick="favKittie(${index})">
        <div class="extra">
          <div class="details">
            <p class="name">${names[index]}</p>
            <p class="age">${moment().diff(birthday, 'weeks')} weeks old</p>
            <p class="age">${ageInWeeks(birthDates[index])} weeks old</p>
          </div>
          <p class="score">${favorites[index]} ❤</p>
        </div>
      </li>
    `})
});

現在重新載入應用程式,並再次查看「Network」面板。

套件大小縮減至 225 KB

套件的大小又縮小了超過一半!

結論

透過本程式碼研究室,您應該已瞭解如何分析特定套件,以及為何移除未使用的或不需要的套件非常實用。開始使用這項技術來改善應用程式之前,請務必瞭解,在較大型應用程式中,這項作業可能會複雜許多

至於移除未使用的程式庫,請嘗試找出套件的哪些部分正在使用,哪些部分未使用。如果有看似無處使用、神秘的套件,請退一步,檢查哪些頂層依附元件可能需要它。請嘗試找出可能的解耦方法。

至於移除不需要的程式庫,情況可能會稍微複雜一些。請務必與團隊密切合作,看看是否有機會簡化部分程式碼集。在這個應用程式中移除 moment 似乎是每次都正確的做法,但如果有需要處理的時區和不同語言代碼呢?或者,如果有更複雜的日期操作,又該如何處理?在操作及剖析日期/時間時,情況可能會變得非常棘手,而 momentdate-fns 等程式庫可大幅簡化這項作業。

凡事都需要權衡利弊,因此請務必評估是否值得投入心力和時間,推出自訂解決方案,而非仰賴第三方程式庫。