收藏、出版和订阅是 Meteor 的一个棘手领域,文档可以更详细地讨论,以避免 frequent confusion 有时会被 confusing terminology 放大。
这是Sacha Greif(DiscoverMeteor 的合著者)在一张幻灯片中解释出版物和订阅:
要正确理解为什么需要多次调用find(),您需要了解集合、发布和订阅在 Meteor 中的工作方式:
您在 MongoDB 中定义集合。还没有涉及流星。这些集合包含 database records(也被 Mongo and Meteor 称为“文档”,但“文档”比数据库记录更通用;例如,更新规范或查询选择器是文档 too - 包含 field: value 对的 JavaScript 对象。
-
然后你在 Meteor 服务器上定义collections
MyCollection = new Mongo.Collection('collection-name-in-mongo')
这些集合包含所有来自 MongoDB 集合的数据,您可以在它们上运行 MyCollection.find({...}),这将返回 cursor(一组记录, 以及遍历它们并返回它们的方法。
-
此光标(大部分时间)用于publish(发送)一组记录(称为“记录集”)。您可以选择仅发布这些记录中的 some 字段。客户 subscribe 访问的是记录集(不是集合)。发布由publish function 完成,每次新客户端订阅时都会调用它,它可以使用参数来管理要返回的记录(例如,用户 id,仅返回该用户的文档)。
李>
-
在客户端,您有Minimongo 集合,这些集合部分反映了一些来自服务器的记录。 “部分”是因为它们可能只包含一些字段,而“一些记录”是因为您通常只想将它需要的记录发送给客户端,以加快页面加载速度,并且只发送它需要的那些和 有访问权限。
Minimongo 本质上是纯 JavaScript 中 Mongo 的内存中非持久实现。它用作本地缓存,仅存储此客户端正在使用的数据库的子集。客户端上的查询(查找)直接从此缓存中提供服务,无需与服务器对话。
这些 Minimongo 集合最初是空的。它们由
填充
Meteor.subscribe('record-set-name')
来电。请注意,subscribe 的参数不是集合名称;它是服务器在publish 调用中使用的记录集 的名称。 subscribe() 调用为客户端订阅记录集 - 服务器集合中的记录子集(例如最近的 100 篇博客文章),每条记录中包含所有字段或字段的子集(例如只有title 和date)。 Minimongo 如何知道将传入记录放入哪个集合?集合的名称将是发布处理程序的 added、changed 和 removed 回调中使用的 collection 参数,或者如果缺少这些(大多数情况下),它将是服务器上 MongoDB 集合的名称。
修改记录
这是 Meteor 让事情变得非常方便的地方:当您在客户端修改 Minimongo 集合中的记录(文档)时,Meteor 会立即更新所有依赖它的模板,并将更改发送回服务器,反过来,它会将更改存储在 MongoDB 中,并将它们发送到已订阅包括该文档在内的记录集的相应客户端。这称为延迟补偿,是seven core principles of Meteor 之一。
多个订阅
您可以拥有一堆拉入不同记录的订阅,但如果来自服务器上的同一个集合,它们最终都会在客户端的同一个集合中,基于他们的_id。这没有解释清楚,但 Meteor 文档暗示了这一点:
当您订阅记录集时,它会告诉服务器将记录发送到客户端。客户端将这些记录存储在本地 Minimongo 集合中,其名称与发布处理程序的 added、changed 和 removed 回调中使用的 collection 参数相同。 Meteor 将对传入的属性进行排队,直到您在客户端上使用匹配的集合名称声明 Mongo.Collection。
没有解释的是,当您不明确使用added、changed 和changed 或根本不发布处理程序时会发生什么 - 大多数情况下。在这种最常见的情况下,collection 参数(不出所料)取自您在步骤 1 中在服务器上声明的 MongoDB 集合的名称。但这意味着您可以拥有不同名称的不同发布和订阅,并且所有记录将在客户端的同一集合中结束。向下到顶级字段的级别,Meteor 负责在文档之间执行集合并集,以便订阅可以重叠 - 发布功能,将不同的顶级字段并排传送到客户端工作客户端,集合中的文档将是union of the two sets of fields。
示例:多个订阅在客户端填充同一个集合
您有一个 BlogPosts 集合,您在服务器和客户端上以相同的方式声明它,即使它做不同的事情:
BlogPosts = new Mongo.Collection('posts');
在客户端,BlogPosts 可以从以下位置获取记录:
-
订阅最近的 10 篇博文
// server
Meteor.publish('posts-recent', function publishFunction() {
return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
}
// client
Meteor.subscribe('posts-recent');
-
订阅当前用户的帖子
// server
Meteor.publish('posts-current-user', function publishFunction() {
return BlogPosts.find({author: this.userId}, {sort: {date: -1}, limit: 10});
// this.userId is provided by Meteor - http://docs.meteor.com/#publish_userId
}
Meteor.publish('posts-by-user', function publishFunction(who) {
return BlogPosts.find({authorId: who._id}, {sort: {date: -1}, limit: 10});
}
// client
Meteor.subscribe('posts-current-user');
Meteor.subscribe('posts-by-user', someUser);
订阅最受欢迎的帖子
- 等
所有这些文档都来自 MongoDB 中的 posts 集合,通过服务器上的 BlogPosts 集合,最终进入客户端上的 BlogPosts 集合。
现在我们可以理解为什么您需要多次调用find() - 第二次是在客户端上,因为来自所有订阅的文档最终会在同一个集合中,您只需要获取您关心的那些.例如,要获取客户端上的最新帖子,您只需从服务器镜像查询:
var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});
这会将光标返回到客户迄今为止收到的所有文档/记录,包括最热门的帖子和用户的帖子。 (thanks Geoffrey)。