将模块捆绑器与 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 生态系统中有许多优质模块捆绑器。本指南重点介绍如何搭配 webpackRollupesbuild 使用 Firebase。

开始

本指南要求您在开发环境中安装 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 文件被视为入口点,因为由它开始导入应用所需的所有代码。模块捆绑器使用该入口点文件开始捆绑流程。

将 Firebase 与 webpack 结合使用

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”配置设置的官方文档。

将 Firebase 与 Rollup 搭配使用

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 构建,请参阅其关于生产环境构建的插件的官方文档。

将 Firebase 与 esbuild 搭配使用

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,请参阅关于缩减大小和其他优化的官方文档。