☰
Firebase JS SDK Realtime Database 的 Database 类:实例获取、内部结构与生命周期全解析
2026/10/15 2:12:12 网站建设 项目流程
  • 开发工具
  • 后端

【免费下载链接】firebase-js-sdk

Firebase Javascript SDK

项目地址:https://gitcode.com/gh_mirrors/fi/firebase-js-sdk
点击查看免费下载

本篇技术指南聚焦 firebase-js-sdk 仓库中@firebase/database模块的核心类Database(即 Firebase Realtime Database 的模块化 SDK 实例)。通过阅读本文,你将掌握Database类的类签名与两个公开属性app、type的含义,理解getDatabase()如何创建并缓存实例、Database与底层Repo的连接关系,以及goOffline()/goOnline()、connectDatabaseEmulator()等配套函数如何作用于该实例,并能在实际项目中正确初始化、复用与销毁 Realtime Database 实例。

Database 类的定位:Realtime Database 的模块化入口

Database类在官方 API 参考文档 database.database.md 中被定义为 “Class representing a Firebase Realtime Database”,即一个代表 Firebase Realtime Database 实例的类。其完整类签名如下:

export declare class Database

在仓库源码 packages/database/src/api/Database.ts#L235-L285 中,该类的实际声明为:

export class Database implements _FirebaseService { /** Represents a `Database` instance. */ readonly 'type' = 'database'; /** Track if the instance has been used (root or repo accessed) */ _instanceStarted: boolean = false; /** Backing state for root_ */ private _rootInternal?: ReferenceImpl; /** @hideconstructor */ constructor( public _repoInternal: Repo, /** The FirebaseApp associated with this Realtime Database instance. */ readonly app: FirebaseApp ) {} // ... }

从源码可以看出两个关键事实:

  • Database实现了_FirebaseService接口,这意味着它是作为 Firebase App 的一个“服务组件”存在,由 Firebase 的组件注册机制统一管理(详见后文“组件注册机制”一节);
  • 它的构造函数带有@hideconstructor标记,不应由开发者直接new创建,而应通过getDatabase()工厂函数获取实例。

公开属性一览:app 与 type

官方文档为Database定义了恰好两个公开属性,下表完整列出:

属性修饰符类型说明
appreadonlyFirebaseApp与该 Realtime Database 实例关联的FirebaseApp。
typereadonly(未声明,字面量类型)表示这是一个Database实例。

Database.app

源码声明:

readonly app: FirebaseApp;

app属性在构造函数中注入(constructor(public _repoInternal: Repo, readonly app: FirebaseApp)),用于保存创建该Database实例时所关联的FirebaseApp。它在整个实例生命周期内不可变更(readonly)。你可以通过它反向访问 Firebase 应用的配置,例如db.app.options.databaseURL、db.app.options.projectId——这些配置在repoManagerDatabaseFromApp()中正是用于解析数据库地址(见后文“实例的创建路径”)。

Database.type

源码声明:

readonly 'type' = 'database';

type是一个字面量类型常量属性,值为字符串'database'。它的作用是为框架层提供运行时类型判别信息:当 Firebase 的组件注册表需要识别某个服务实例的类型时,可以通过instance.type === 'database'进行校验。类似的type字段在 Firebase 其他服务的实例上也普遍存在,是模块化 SDK 中常见的实例标识模式。

获取 Database 实例:getDatabase() 工厂函数

由于构造函数被隐藏,获取Database实例的唯一标准途径是调用getDatabase():

export declare function getDatabase(app?: FirebaseApp, url?: string): Database;

参数说明:

参数类型说明
appFirebaseApp(可选)关联的 FirebaseApp 实例,省略时默认使用默认 App(getApp())。
urlstring(可选)要连接的 Realtime Database 实例 URL;省略时 SDK 连接该 App 的默认实例。

对应源码位于 packages/database/src/api/Database.ts#L323-L337:

export function getDatabase( app: FirebaseApp = getApp(), url?: string ): Database { const db = _getProvider(app, 'database').getImmediate({ identifier: url }) as Database; if (!db._instanceStarted) { const emulator = getDefaultEmulatorHostnameAndPort('database'); if (emulator) { connectDatabaseEmulator(db, ...emulator); } } return db; }

典型用法:

import { initializeApp } from 'firebase/app'; import { getDatabase, ref } from 'firebase/database'; const app = initializeApp({ databaseURL: 'https://my-app-default-rtdb.firebaseio.com' }); const db = getDatabase(app); // 默认数据库实例 const db2 = getDatabase(app, 'https://my-app-other-rtdb.firebaseio.com'); // 指定 URL 的独立实例

需要特别注意两点:

  1. url参数即组件标识符:getDatabase()通过_getProvider(app, 'database').getImmediate({ identifier: url })获取实例,url被用作组件实例的instanceIdentifier。这意味着同一个 App 下,不同 URL 会创建不同的Database实例;而对同一 URL 重复调用则返回已缓存的同一实例。
  2. 重复初始化保护:在底层repoManagerCreateRepo()中,如果同一 App + 同一 URL 对应的Repo已存在,会抛出错误 “Database initialized multiple times. Please make sure the format of the database URL matches with each database() call.”(见 packages/database/src/api/Database.ts#L200-L223),因此请保证对同一数据库只使用一致的 URL 格式调用一次。

实例的创建路径:repoManagerDatabaseFromApp

getDatabase()最终调用的是组件工厂函数repoManagerDatabaseFromApp()(packages/database/src/api/Database.ts#L118-L177),其流程可以概括为:

  1. 确定数据库地址:优先使用显式传入的url,其次使用app.options.databaseURL;若两者都缺失,则回退为${projectId}-default-rtdb.firebaseio.com;若连projectId也没有,直接抛出致命错误 “Can't determine Firebase Database URL…”。
  2. 解析 URL:通过parseRepoInfo()将 URL 解析为RepoInfo(host、namespace、是否使用 SSL 等信息,见 packages/database/src/core/RepoInfo.ts#L32-L99)。
  3. 模拟器自动切换:若进程环境中存在FIREBASE_DATABASE_EMULATOR_HOST环境变量,会优先切换为http://${dbEmulatorHost}?ns=${namespace}形式的模拟器地址。
  4. 校验 URL:validateUrl()校验 URL 合法性,且要求 URL 必须指向数据库根节点(“Database URL must point to the root of a Firebase Database (not including a child path).”)。
  5. 创建 Repo 并返回实例:调用repoManagerCreateRepo()创建/获取Repo,再return new Database(repo, app)。

从中可以明确Database与Repo的关系:Database是面向开发者的轻量门面(facade),而真正负责网络连接、数据同步、查询派发等核心逻辑的是内部的Repo。Database._repoInternal保存的就是这个Repo。

Database 的内部结构与生命周期管理

除了两个公开属性,Database类还包含一组带下划线前缀的内部成员(不属于公开 API,但理解它们有助于把握实例生命周期):

内部成员类型作用
_repoInternalRepo底层 Repo,负责与服务器通信与数据同步。
_instanceStartedboolean标记实例是否已启动(首次访问_repo时置为true)。
_rootInternalReferenceImpl根节点引用(Reference),首次访问_root时创建。
_delete()Promise<void>删除实例并断开底层 Repo 连接。
_checkNotDeleted(apiName)void在已删除实例上调用 API 时抛出致命错误。

延迟启动机制

_repogetter 体现了“懒启动”设计(packages/database/src/api/Database.ts#L252-L262):

get _repo(): Repo { if (!this._instanceStarted) { repoStart( this._repoInternal, this.app.options.appId, this.app.options['databaseAuthVariableOverride'] ); this._instanceStarted = true; } return this._repoInternal; }

只有在第一次真正访问_repo或_root(例如调用ref(db)、建立监听)时,repoStart()才会被触发。repoStart()(packages/database/src/core/Repo.ts#L211-L284)负责创建PersistentConnection(或测试场景下的ReadonlyRestClient)、挂接认证 token 变更监听与 App Check token 监听,从而正式建立与服务器的连接。

_rootgetter 则在首次访问时创建指向根路径(空路径)的ReferenceImpl:

get _root(): ReferenceImpl { if (!this._rootInternal) { this._rootInternal = new ReferenceImpl(this._repo, newEmptyPath()); } return this._rootInternal; }

删除与防误用

  • _delete()会调用repoManagerDeleteRepo()(packages/database/src/api/Database.ts#L183-L191),后者执行repoInterrupt(repo)断开连接,并从缓存的repos表中移除该 Repo;
  • 删除之后,_checkNotDeleted()会拦截所有后续操作并抛出 “Cannot call … on a deleted database.” 致命错误。

组件注册机制:Database 如何挂载到 Firebase App

Database作为服务组件,通过 packages/database/src/register.ts 注册到 Firebase 的组件注册表:

_registerComponent( new Component( 'database', (container, { instanceIdentifier: url }) => { const app = container.getProvider('app').getImmediate()!; const authProvider = container.getProvider('auth-internal'); const appCheckProvider = container.getProvider('app-check-internal'); return repoManagerDatabaseFromApp(app, authProvider, appCheckProvider, url); }, ComponentType.PUBLIC ).setMultipleInstances(true) );

关键点:

  • 组件名称为'database',ComponentType.PUBLIC表示面向开发者公开;
  • setMultipleInstances(true)允许同一 App 下存在多个Database实例(这正是getDatabase(app, url)中不同 URL 对应不同实例的机制来源);
  • 工厂函数从容器中取出app、auth-internal、app-check-internal三个 Provider 后交给repoManagerDatabaseFromApp,因此Database实例天然集成了 Firebase Authentication 与 App Check 的 token 提供能力。

模块入口 packages/database/src/index.ts 在导入公共 API 后立即执行registerDatabase(),完成自注册。公共 API 的完整导出清单见 packages/database/src/api.standalone.ts。

围绕 Database 实例的配套函数

Database实例是下面这些顶层函数的直接操作对象,它们共同构成对连接生命周期与传输方式的管理能力。

goOffline() 与 goOnline():主动控制连接

export declare function goOffline(db: Database): void; export declare function goOnline(db: Database): void;
  • goOffline(db):断开与服务器的连接。客户端默认会维持一条持久的自动重连连接;在离线期间,本地写入操作仍会立即触发事件,并被排队等待重连后自动重试(源码见 packages/database/src/api/Database.ts#L423-L427)。
  • goOnline(db):重新连接并同步离线期间累积的状态,使客户端自动“追赶”上服务器(packages/database/src/api/Database.ts#L440-L444)。

两者内部最终分别调用repoInterrupt()/repoResume()(packages/database/src/core/Repo.ts#L836-L846),作用于PersistentConnection的 interrupt/resume。

connectDatabaseEmulator():接入模拟器

export declare function connectDatabaseEmulator( db: Database, host: string, port: number, options?: { mockUserToken?: EmulatorMockTokenOptions | string; } ): void;

注意:该方法必须在任何其他数据库操作之前调用。源码中(packages/database/src/api/Database.ts#L350-L400)会检查db._instanceStarted:若实例已启动且参数不同,会直接抛出致命错误 “connectDatabaseEmulator() cannot initialize or alter the emulator configuration after the database instance has started.”。options.mockUserToken可用于在单元测试 Security Rules 时伪造认证 token(对应 util 文档 中的EmulatorMockTokenOptions)。

forceWebSockets() / forceLongPolling():传输层选择

export declare function forceWebSockets(): void; export declare function forceLongPolling(): void;
  • forceWebSockets():强制使用 WebSocket 传输,内部调用BrowserPollConnection.forceDisallow();
  • forceLongPolling():强制使用 long-polling 传输(若 databaseURL 中已使用 websocket 协议则会被忽略),内部调用WebSocketConnection.forceDisallow()与BrowserPollConnection.forceAllow()。

两个函数都会先经checkTransportInit()检查:如果传输层已初始化会发出警告。实现位于 packages/database/src/api/Database.ts#L287-L310。

enableLogging():调试日志开关

export declare function enableLogging(enabled: boolean, persistent?: boolean): any; export declare function enableLogging(logger: (message: string) => unknown): any;

两种重载分别支持“布尔开关”(persistent为true时刷新页面后仍记住日志状态)与“自定义 logger 函数”,最终委托给enableLoggingImpl()(packages/database/src/api/Database.ts#L453-L467)。

与 Database 相关的其他核心类型

在 database.md 的模块总览中,Database类与以下类型共同构成@firebase/database的公共 API 面,了解它们有助于整体把握Database的使用场景:

  • DatabaseReference:表示数据库中某个具体位置,用于读写数据,通过ref(db, path)或child(parent, path)获得;
  • Query / QueryConstraint:对数据进行排序与过滤,由orderByChild()、limitToFirst()、startAt()/endAt()/equalTo()等约束组合生成;
  • DataSnapshot:读取数据时收到的不可变数据快照,通过val()提取内容;
  • OnDisconnect:客户端断线时自动写入/清除数据,常用于在线状态(presence)检测;
  • ThenableReference:push()返回的“既是 Promise 又是 Reference”的特殊类型,可立即当引用使用,也可 await 等待写入完成;
  • runTransaction():原子化地修改数据,配合TransactionOptions/TransactionResult使用。

其中push()生成的唯一键按当前时间排序且包含 72 位随机熵,用于向列表追加数据时既按时间有序又难以被猜测(实现见 packages/database/src/core/util/NextPushId.ts 对应的nextPushId)。

小结与源码阅读指引

Database类是@firebase/database模块中连接“Firebase App 组件体系”与“Realtime Database 底层同步引擎”的枢纽:开发者通过getDatabase()获得它,通过它的app属性访问关联 App、通过type属性识别实例身份,而它内部持有的_repoInternal(Repo)则承担了全部网络通信与数据同步职责。掌握该类的实例化路径、懒启动机制与生命周期管理,是正确使用 Firebase Realtime Database 模块化 SDK 的前提。

如需深入源码,推荐按以下顺序阅读:

  • 类定义与实例工厂:packages/database/src/api/Database.ts
  • 组件注册:packages/database/src/register.ts
  • 底层同步引擎:packages/database/src/core/Repo.ts
  • 连接信息解析:packages/database/src/core/RepoInfo.ts
  • 模块入口与导出:packages/database/src/index.ts、packages/database/src/api.standalone.ts
  • 官方 API 参考:docs-devsite/database.database.md(本主题文档)、docs-devsite/database.md(模块总览)
  • 开发工具
  • 后端

【免费下载链接】firebase-js-sdk

Firebase Javascript SDK

项目地址:https://gitcode.com/gh_mirrors/fi/firebase-js-sdk
点击查看免费下载
上一篇:SiYuan字体替换终极指南:3步统一文档风格,打造专属知识管理体验
下一篇:kOps 删除 InstanceGroup 实战指南:kops delete instancegroup 命令全解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询