移除用不到的程式碼

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

應用程式螢幕截圖

測量

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

  • 如要預覽網站,請按下「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 壓縮大小 套件使用 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';

如今,應用程式重新載入時,開發人員工具警告就不會顯示。開啟開發人員工具「網路」面板也會顯示「大幅」縮減套件大小:

套件大小縮減至 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 等程式庫可大幅簡化這項作業。

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