移除未使用的程式碼

Houssein Djirdeh
Houssein Djirdeh

在本程式碼研究室中,請移除所有未使用的多餘依附元件,藉此提升下列應用程式的效能。

應用程式螢幕截圖

測量

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

  • 如要預覽網站,請按下「查看應用程式」,然後按下「全螢幕」圖示 全螢幕

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

  1. 按下 `Control+Shift+J` 鍵 (在 Mac 上為 `Command+Option+J` 鍵) 開啟開發人員工具。
  2. 按一下 [網路] 分頁標籤。
  3. 選取「停用快取」核取方塊。
  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 版 (本應用程式使用此版本) 會自動縮減編譯後的檔案,因此檔案大小會小於統計資料大小。
Gzipped size 以 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 UTC 起經過的毫秒數表示。如果可以計算出目前日期和時間的格式,或許就能建構一個小型函式,找出每隻小貓的週齡。

請盡量不要複製及貼上這裡的內容。首先,請從 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 等程式庫可大幅簡化這項作業。

所有事物都是一種取捨,因此請務必評估是否值得投入複雜度和心力,推出自訂解決方案,而不是依賴第三方程式庫。