
开发工具后端【免费下载链接】firebase-js-sdkFirebase Javascript SDK项目地址https://gitcode.com/gh_mirrors/fi/firebase-js-sdk点击查看免费下载DocumentChange是 Firebase Web SDKfirebase-js-sdk中 Cloud Firestore 查询实时监听的核心数据结构当查询结果集发生增、删、改时QuerySnapshot.docChanges()会返回一组DocumentChange对象精确描述哪个文档变了、怎么变的、变之前后它在结果集中的位置。本文基于仓库中的接口定义文档 firestore.documentchange.md结合packages/firestore/src/api/snapshot.ts的真实实现与集成测试完整讲解DocumentChange的四个属性、三种变更类型、索引oldIndex/newIndex的语义边界以及includeMetadataChanges选项的约束帮助你在实时同步场景中正确消费变更事件。一、接口签名与整体定位DocumentChange是一个 TypeScript 泛型接口定义于 packages/firestore/src/api/snapshot.ts其完整声明如下export interface DocumentChange AppModelType DocumentData, DbModelType extends DocumentData DocumentData { /** The type of change (added, modified, or removed). */ readonly type: DocumentChangeType; /** The document affected by this change. */ readonly doc: QueryDocumentSnapshotAppModelType, DbModelType; /** * The index of the changed document in the result set immediately prior to * this DocumentChange (i.e. supposing that all prior DocumentChange objects * have been applied). Is -1 for added events. */ readonly oldIndex: number; /** * The index of the changed document in the result set immediately after * this DocumentChange (i.e. supposing that all prior DocumentChange * objects and the current DocumentChange object have been applied). * Is -1 for removed events. */ readonly newIndex: number; }其中AppModelType应用模型类型与DbModelType数据库模型类型是 firebase-js-sdk 引入的类型化数据转换双泛型设计AppModelType是你业务层使用的对象类型DbModelType是 Firestore 数据库中存储的文档数据结构。二者默认均为DocumentData键值对对象。通过 FirestoreDataConverter 可以实现两类模型之间的自动互转而DocumentChange的doc属性会携带这套类型参数一路传递保证change.doc.data()返回的是转换后的AppModelType。二、四个核心属性逐一解析接口共含四个只读属性全部来自官方文档 firestore.documentchange.md下表汇总属性类型含义typeDocumentChangeType变更类型added、modified或removeddocQueryDocumentSnapshotAppModelType, DbModelType受该变更影响的文档快照oldIndexnumber该文档在应用了本变更之前的结果集中的下标added事件恒为-1newIndexnumber该文档在应用了本变更之后的结果集中的下标removed事件恒为-12.1type三种变更类型DocumentChangeType在 packages/firestore/src/api/snapshot.ts 中定义export type DocumentChangeType added | removed | modified;added文档进入查询结果集新创建、或新匹配上查询条件、或首次快照时全部文档均按added上报modified文档仍匹配查询但数据或排序位置发生变化removed文档离开结果集被删除、或不再匹配查询条件。在底层实现中视图层View维护的变更类型枚举还额外包含Metadata一类见 packages/firestore/src/core/view_snapshot.ts。对外暴露时Metadata类型的变更会通过resultChangeType函数映射为modified见 snapshot.ts 中的 resultChangeType——这也解释了为什么纯元数据变化如hasPendingWrites、fromCache翻转在语义上被合并进modified。2.2doc变更对应的文档快照doc的类型是QueryDocumentSnapshot而不是普通的DocumentSnapshot。两者的区别在于QueryDocumentSnapshot保证文档一定存在因此data()永不返回undefinedexists恒为true见 QueryDocumentSnapshot 类的源码注释。由于变更列表只针对仍或曾在结果集中的文档所以使用QueryDocumentSnapshot是类型层面的正确表达。你可以通过change.doc.id、change.doc.ref拿到文档 ID 与引用通过change.doc.data()读取字段通过change.doc.metadata.hasPendingWrites判断该变更是否来自本地未同步的写入。2.3oldIndex与newIndex索引语义边界索引是DocumentChange最容易被误用的部分官方文档给出了两条硬性规则oldIndex应用本变更之前该文档在结果集中的下标added事件恒为-1因为之前它根本不在结果集里newIndex应用本变更之后该文档在结果集中的下标removed事件恒为-1因为之后它已不在结果集里。需要特别强调的是这两个索引都是假设前面的DocumentChange已依次应用之后的位置因此索引的计算是有先后依赖的docChanges()返回的数组中第 i 个变更的索引反映的是第 1..i-1 个变更已生效的状态。这保证了你可以在一次回调内按顺序重放全部变更逐步重建出最新结果集。从源码看索引计算逻辑位于 changesFromSnapshotlet oldIndex -1; let newIndex -1; if (change.type ! ChangeType.Added) { oldIndex indexTracker.indexOf(change.doc.key); debugAssert(oldIndex 0, Index for document not found); indexTracker indexTracker.delete(change.doc.key); } if (change.type ! ChangeType.Removed) { indexTracker indexTracker.add(change.doc); newIndex indexTracker.indexOf(change.doc.key); }即非added变更先从增量追踪集合indexTracker中查出旧下标并移除非removed变更再按查询排序newQueryComparator把文档插回集合从而得到新下标。indexTracker初始化为上一次快照的文档集合oldDocsViewSnapshot.oldDocs见 ViewSnapshot 定义。此外还有一个快速路径当oldDocs为空即首次快照时所有变更必然都是added此时直接按顺序赋oldIndex: -1、newIndex: index并断言事件顺序严格递增见 changesFromSnapshot 的首快照分支。三、获取 DocumentChangeQuerySnapshot.docChanges() 与缓存机制DocumentChange对象的唯一公开入口是QuerySnapshot.docChanges(options)它由QuerySnapshot类提供实现在 packages/firestore/src/api/snapshot.tsdocChanges( options: SnapshotListenOptions {} ): ArrayDocumentChangeAppModelType, DbModelType { const includeMetadataChanges !!options.includeMetadataChanges; if (includeMetadataChanges this._snapshot.excludesMetadataChanges) { throw new FirestoreError( Code.INVALID_ARGUMENT, To include metadata changes with your document changes, you must also pass { includeMetadataChanges:true } to onSnapshot(). ); } // 结果被缓存到 _cachedChanges按 includeMetadataChanges 区分缓存键 if ( !this._cachedChanges || this._cachedChangesIncludeMetadataChanges ! includeMetadataChanges ) { this._cachedChanges changesFromSnapshot(this, includeMetadataChanges); this._cachedChangesIncludeMetadataChanges includeMetadataChanges; } return this._cachedChanges; }需要注意两点实现事实结果缓存docChanges()的计算结果按includeMetadataChanges取值缓存_cachedChanges/_cachedChangesIncludeMetadataChanges见 snapshot.ts 第 727-728 行同一快照上重复调用不会重复计算非法调用会抛错若你在onSnapshot()监听时没有开启includeMetadataChanges: true却在docChanges({ includeMetadataChanges: true })中要求包含元数据变更SDK 会抛出INVALID_ARGUMENT错误。因为快照视图层此时根本没有采集元数据变更ViewSnapshot.excludesMetadataChanges为真这个约束是出于正确性的强制校验。官方文档对docChanges()的行为描述是返回自上次快照以来发生的文档变更数组若是首次快照则全部文档都会以added形式出现在列表中见 firestore.querydocumentsnapshot.md 对应说明。四、完整可运行示例实时监听与变更消费下面给出一个可直接复制到 Web 项目中运行的完整示例演示如何用onSnapshotdocChanges()消费三类变更import { initializeApp } from firebase/app; import { getFirestore, collection, onSnapshot, query, orderBy, QuerySnapshot, DocumentChange } from firebase/firestore; // 1. 初始化 const app initializeApp({ /* 你的 Firebase 配置 */ }); const db getFirestore(app); // 2. 监听按 order 字段排序的查询 const coll collection(db, items); const q query(coll, orderBy(order)); const unlisten onSnapshot(q, (snapshot: QuerySnapshot) { snapshot.docChanges().forEach((change: DocumentChange) { const data change.doc.data(); const label ${change.doc.id} (order${data.order}); switch (change.type) { case added: console.log(新增文档 ${label}位于新结果集下标 ${change.newIndex}); break; case modified: console.log( 修改文档 ${label}旧下标 ${change.oldIndex} → 新下标 ${change.newIndex} ); break; case removed: console.log(删除文档 ${label}移除前下标为 ${change.oldIndex}); break; } }); }); // 3. 不再需要时取消监听 // unlisten();索引语义的直观验证以集成测试 query.test.ts 中的 maintains correct DocumentChange indexes 为例初始集合{a: order 1, b: order 2, c: order 3}按order升序监听首次快照3 个变更均为addedoldIndex-1newIndex依次为0/1/2把 b 的 order 改为 4仅 1 个变更b的oldIndex1、newIndex2排序后 b 移到 c 之后删除 c仅 1 个变更c的oldIndex1、newIndex-1。测试断言函数verifyDocumentChangequery.test.ts 第 2075-2086 行逐项校验doc.id、type、oldIndex、newIndex这与官方文档中的索引定义完全一致可作为理解索引语义的权威验证用例。类似的变更消费模式也出现在packages/firestore/test/integration/util/helpers.ts的docSet.docChanges(options).map(d d.doc.data())工具函数中helpers.ts 第 246 行。五、注意事项与边界情况oldIndex/newIndex是针对查询结果集的排序下标不是数据库主键。下标以当前查询的排序规则orderBy无排序时按文档 ID 排序为准且随查询条件不同而不同。-1的语义是不存在added的oldIndex-1、removed的newIndex-1modified则两个索引通常都是有效非负数不要对-1做数组越界访问。元数据变更合并为modifiedincludeMetadataChanges: true时hasPendingWrites、fromCache等仅元数据变化会以modified上报底层ChangeType.Metadata被映射详见 resultChangeType。必须与onSnapshot选项保持一致要在docChanges()中开启includeMetadataChanges监听时必须同时传入{ includeMetadataChanges: true }否则抛出INVALID_ARGUMENT见 snapshot.ts 第 806-811 行。首快照全为added即使文档在首次监听前就存在第一次回调中它们也统一以added类型上报这是官方文档明确的行为也是changesFromSnapshot首快照分支的快速路径假设。六、关联 API 速查DocumentChange并非孤立存在它与以下 API 协同工作可在仓库文档中继续深挖QueryDocumentSnapshotdoc属性的具体类型保证文档存在QuerySnapshotdocChanges()的来源提供docs、size、forEach等配套能力FirestoreDataConverter驱动AppModelType/DbModelType双泛型的数据转换SnapshotListenOptions控制includeMetadataChanges的监听选项DocumentSnapshotQueryDocumentSnapshot的父类提供data()、get(field)、metadata等基础能力。以上接口的官方类型定义与签名均可在仓库 docs-devsite 目录下的对应文档中找到其实现与测试则分别位于 packages/firestore/src/api/snapshot.ts 与 packages/firestore/test/integration/api/query.test.ts是深入理解 Firestore 实时同步机制的首选源码入口。赞分享开发工具后端【免费下载链接】firebase-js-sdkFirebase Javascript SDK项目地址https://gitcode.com/gh_mirrors/fi/firebase-js-sdk点击查看免费下载相关推荐Firebase JS SDK 聚合查询详解深入理解 Firestore 的 AggregateField 类Firebase JS SDK 聚合查询详解深入理解 Firestore 的 AggregateField 类 导读 AggregateField 是 Fir开发工具后端firebase-js-sdk 中 LanguageModelExpected 接口详解为 firebase/ai 端侧语言模型声明多模态能力firebase js sdk 中 LanguageModelExpected 接口详解为 firebase/ai 端侧语言模型声明多模态能力 本篇技术指南开发工具后端Firebase JS SDK Analytics EventParams 接口全解析GA4 标准事件参数的类型化使用指南Firebase JS SDK Analytics EventParams 接口全解析GA4 标准事件参数的类型化使用指南 EventParams 是 Fir开发工具后端上一篇Windows系统优化终极指南Win11Debloat深度解析与实战应用下一篇CANN/cannbot-skills本地矩阵乘加操作创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考