使用 `TransferState` 将服务器渲染的 Firestore 数据传输给浏览器,而不是每次都要读取两次文档
作者: armando-navarro创建于 2026年8月31日更新于 2026年9月2日
标签type: featurecomp: firestorecomp: ssrpriority: P0 (critical)version: current (v17+)
目前用户遇到的问题
- 服务器渲染的内容在应用启动后消失了 319 到 506 毫秒,直到浏览器自己的读取返回为止。
- 服务器上读取的每个文档都在浏览器中再次读取,因此每个文档的页面需要两次读取而不是一次。
- 解决办法是手动为每个组件写入数据。此仓库中的示例应用在五个组件中做到了这一点,其中两个是 Firestore 读取,而 #3323 则是八个注释,其中人员使用混合结果执行相同操作。
建议的方案
应用添加 provideFirestoreTransfer() 并标记需要传输的读取操作:
docData(ref, { transfer: true })将文档的值存储在服务器上,并在浏览器中以读取的第一个值发送,从而使渲染内容保持在屏幕上。collectionData(query, { transfer: true })将查询的序列化快照(QuerySnapshot.toJSON())存储起来,其中包含查询本身,浏览器可使用querySnapshotFromJSON重新构建真实的快照。- 未指定选项的读取操作以及不添加提供程序的应用中的每个读取操作都与当前行为完全一致。
对于没有
limit子句的查询,浏览器随后使用onSnapshotResume将其监听器添加到后端,该监听器要求后端返回自服务器读取以来的变化,而不是再次读取文档。正是这种方式消除了第二次读取。
此方案不会做到的事情
- 带有
limit子句的查询保留了其第二次读取。恢复受限查询使后端重新发送整个结果集,这表明比不进行传输更糟糕,因此库将传输第一次渲染时的数据,并添加普通监听器。是否可以支持受限查询是 Firestore 团队的问题。 - 聚合读取,例如
getCountFromServer,无法参与,因为它们的结果没有公开的序列化形式。 - 不存在的文档的读取操作被跳过。页面中没有可放入的值,而且 Firestore 对不存在的文档的序列化形式尝试在浏览器中重建时会抛出错误。
limitToLast查询被跳过。序列化查询没有记录从末尾计算的限制,因此浏览器将重建不再与应用运行的查询匹配的查询,并且文档将以错误的顺序返回。- 数据…
内容来源: angular/angularfire