搭配 Firebase 使用模組組合器

JavaScript 模組打包工具可以執行許多作業,但其中一項最實用的功能,就是在程式碼庫中新增及使用外部程式庫。模組組合器會讀取程式碼中的匯入路徑,並將應用程式專屬程式碼與匯入的程式庫程式碼合併 (組合)。

從第 9 版開始,Firebase JavaScript 模組化 API 經過最佳化,可搭配模組打包工具的最佳化功能,減少最終建構中包含的 Firebase 程式碼量。

import { initializeApp } from 'firebase/app';
import { getAuth, onAuthStateChanged, getRedirectResult } from 'firebase/auth';

const firebaseApp = initializeApp({ /* config */ });
const auth = getAuth(firebaseApp);
onAuthStateChanged(auth, user => { /* check status */ });

/**
 * getRedirectResult is unused and should not be included in the code base.
 * In addition, there are many other functions within firebase/auth that are
 * not imported and therefore should not be included as well.
 */

從程式庫中移除未使用的程式碼,這個程序稱為「搖樹」。手動移除這段程式碼非常耗時且容易出錯,但模組打包工具可以自動移除。

JavaScript 生態系統中有許多優質的模組打包工具。本指南主要介紹如何搭配使用 Firebase 與 webpackRollupesbuild

開始使用

本指南假設您已在開發環境中安裝 npm。npm 用於安裝及管理依附元件 (程式庫)。如要安裝 npm,請安裝 Node.js,其中會自動包含 npm。

安裝 Node.js 後,大多數開發人員都能順利完成設定。不過,許多開發人員在設定環境時,都會遇到常見問題。如果遇到任何錯誤,請確認環境已安裝 npm CLI,並設定適當權限,不必以管理員身分透過 sudo 指令安裝套件

package.json 和安裝 Firebase

安裝 npm 後,您需要在本機專案的根目錄建立 package.json 檔案。使用下列 npm 指令產生這個檔案:

npm init

系統會透過精靈引導您提供所需資訊。建立檔案後,檔案內容會類似如下:

{
  "name": "your-package-name",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {

  }
}

這個檔案負責許多不同的工作,如要進一步瞭解模組組合和建構 JavaScript 程式碼,請務必熟悉這個重要檔案。本指南的重要部分是 "dependencies" 物件。這個物件會保留您安裝的程式庫和所用版本的鍵/值組合。

如要新增依附元件,請使用 npm installnpm i 指令。

npm i firebase

執行 npm i firebase 時,安裝程序會更新 package.json,將 Firebase 列為依附元件:

  "dependencies": {
    "firebase": "^9.0.0"
  },

鍵是程式庫的名稱,值則是使用的版本。版本值很靈活,可接受一系列值。這就是所謂的語意化版本控制或 semver。如要進一步瞭解 semver,請參閱 npm 的語意版本管理指南

來源資料夾與建構資料夾

您編寫的程式碼會由模組打包工具讀取及處理,然後輸出為新檔案或一組檔案。請務必將這兩種檔案分開。 模組打包工具讀取及處理的程式碼稱為「來源」程式碼。這些檔案的輸出內容稱為建構或「dist」(發布) 程式碼。

程式碼庫的常見設定是將原始碼儲存在名為 src 的資料夾中,並將建構的程式碼儲存在名為 dist 的資料夾中。

- src
 |_ index.js
 |_ animations.js
 |_ datalist.js


- dist
 |_ bundle.js

在上述範例檔案結構中,請注意 index.js 會同時匯入 animations.jsdatalist.js。模組打包工具處理原始碼時,會在 dist 資料夾中產生 bundle.js 檔案。bundle.jssrc 資料夾中的檔案和匯入的任何程式庫組合。

如果您使用 Git 等原始碼控管系統,將程式碼儲存在主要存放區時,通常會忽略 dist 資料夾。

進入點

模組打包工具都有進入點的概念。您可以將應用程式視為檔案樹狀結構。一個檔案會從另一個檔案匯入程式碼,依此類推。也就是說,一個檔案會是樹狀結構的根。這個檔案稱為進入點。

讓我們回顧先前的檔案結構範例。

- src
 |_ index.js
 |_ animations.js
 |_ datalist.js


- dist
 |_ bundle.js
// src/index.js
import { animate } from './animations';
import { createList } from './datalist';

// This is not real code, but for example purposes only
const theList = createList('users/123/tasks');
theList.addEventListener('loaded', event => {
  animate(theList);
});

src/index.js 檔案會開始匯入應用程式所需的所有程式碼,因此視為進入點。模組打包工具會使用這個進入點檔案,開始打包程序。

搭配 webpack 使用 Firebase

Firebase 應用程式和 webpack 無須進行特定設定。這個部分涵蓋一般 webpack 設定

第一步是從 npm 安裝 webpack 做為開發依附元件。

npm i webpack webpack-cli -D

在本地專案的根目錄中建立名為 webpack.config.js 的檔案,並新增下列程式碼。

const path = require('path');

module.exports = {
  // The entry point file described above
  entry: './src/index.js',
  // The location of the build folder described above
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  // Optional and for development only. This provides the ability to
  // map the built code back to the original source format when debugging.
  devtool: 'eval-source-map',
};

然後確認您已將 Firebase 安裝為依附元件。

npm i firebase

接著在程式碼庫中初始化 Firebase。下列程式碼會在進入點檔案中匯入及初始化 Firebase,並使用 Firestore Lite 載入「city」文件。

// src/index.js
import { initializeApp } from 'firebase/app';
import { getFirestore, doc, getDoc } from 'firebase/firestore/lite';

const firebaseApp = initializeApp({ /* config */ });
const db = getFirestore(firebaseApp);

async function loadCity(name) {
  const cityDoc = doc(db, `cities/${name}`);
  const snapshot = await getDoc(cityDoc);
  return {
    id: snapshot.id,
    ...snapshot.data(),
  };
}

下一步是新增 npm 指令碼,執行 webpack 建構作業。開啟 package.json 檔案,然後將下列鍵/值組合新增至 "scripts" 物件。

  "scripts": {
    "build": "webpack --mode=development"
  },

如要執行 webpack 並產生建構資料夾,請執行下列指令。

npm run build

最後,請檢查 dist 建構資料夾。其中應包含名為 bundle.js 的檔案,內含您已組合的應用程式和依附元件程式碼。

如要進一步瞭解如何為正式版最佳化 webpack 建構作業,請參閱 「mode」設定的官方說明文件。

搭配 Rollup 使用 Firebase

Firebase 應用程式和 Rollup 無須進行特定設定。本節說明一般的 Rollup 設定。

第一步是安裝 Rollup 和外掛程式,用於將匯入內容對應至透過 npm 安裝的依附元件。

npm i rollup @rollup/plugin-node-resolve -D

在您本機專案的根目錄中,建立名為 rollup.config.js 的檔案,並加入下列程式碼。

import { nodeResolve } from '@rollup/plugin-node-resolve';

export default {
  // the entry point file described above
  input: 'src/index.js',
  // the output for the build folder described above
  output: {
    file: 'dist/bundle.js',
    // Optional and for development only. This provides the ability to
    // map the built code back to the original source format when debugging.
    sourcemap: 'inline',
    // Configure Rollup to convert your module code to a scoped function
    // that "immediate invokes". See the Rollup documentation for more
    // information: https://rollupjs.org/guide/en/#outputformat
    format: 'iife'
  },
  // Add the plugin to map import paths to dependencies
  // installed with npm
  plugins: [nodeResolve()]
};

接著在程式碼庫中初始化 Firebase。下列程式碼會在進入點檔案中匯入及初始化 Firebase,並使用 Firestore Lite 載入「city」文件。

// src/index.js
import { initializeApp } from 'firebase/app';
import { getFirestore, doc, getDoc } from 'firebase/firestore/lite';

const firebaseApp = initializeApp({ /* config */ });
const db = getFirestore(firebaseApp);

async function loadCity(name) {
  const cityDoc = doc(db, `cities/${name}`);
  const snapshot = await getDoc(cityDoc);
  return {
    id: snapshot.id,
    ...snapshot.data(),
  };
}

下一步是新增 npm 指令碼,執行 rollup 建構作業。開啟 package.json 檔案,然後將下列鍵/值組合新增至 "scripts" 物件。

  "scripts": {
    "build": "rollup -c rollup.config.js"
  },

如要執行 Rollup 並產生建構資料夾,請執行下列指令。

npm run build

最後,請檢查 dist 建構資料夾。其中應包含名為 bundle.js 的檔案,內含您已組合的應用程式和依附元件程式碼。

如要進一步瞭解如何為正式版最佳化 Rollup 建構作業,請參閱正式版建構作業外掛程式的官方說明文件

搭配 esbuild 使用 Firebase

Firebase 應用程式和 esbuild 無須進行特定設定。本節將介紹一般的 esbuild 設定。

第一步是將 esbuild 安裝為開發依附元件。

npm i esbuild -D

在本地專案的根目錄中建立名為 esbuild.config.js 的檔案,並新增下列程式碼。

require('esbuild').build({
  // the entry point file described above
  entryPoints: ['src/index.js'],
  // the build folder location described above
  outfile: 'dist/bundle.js',
  bundle: true,
  // Replace with the browser versions you need to target
  target: ['chrome60', 'firefox60', 'safari11', 'edge20'],
  // Optional and for development only. This provides the ability to
  // map the built code back to the original source format when debugging.
  sourcemap: 'inline',
}).catch(() => process.exit(1))

接著在程式碼庫中初始化 Firebase。下列程式碼會在進入點檔案中匯入及初始化 Firebase,並使用 Firestore Lite 載入「city」文件。

// src/index.js
import { initializeApp } from 'firebase/app';
import { getFirestore, doc, getDoc } from 'firebase/firestore/lite';

const firebaseApp = initializeApp({ /* config */ });
const db = getFirestore(firebaseApp);

async function loadCity(name) {
  const cityDoc = doc(db, `cities/${name}`);
  const snapshot = await getDoc(cityDoc);
  return {
    id: snapshot.id,
    ...snapshot.data(),
  };
}

下一步是新增 npm 指令碼,以執行 esbuild。開啟 package.json 檔案,然後將下列鍵/值組合新增至 "scripts" 物件。

  "scripts": {
    "build": "node ./esbuild.config.js"
  },

最後,請檢查 dist 建構資料夾。其中應包含名為 bundle.js 的檔案,內含您已組合的應用程式和依附元件程式碼。

如要進一步瞭解如何最佳化 esbuild 以用於正式版,請參閱官方的縮減和其他最佳化文件。