集合、发布和订阅是Meteor中一个棘手的领域,文档可以更详细地讨论这些问题,以避免频繁 混淆,有时由于混淆的术语而被放大。
这里是Sacha Greif(DiscoverMeteor的合著者)在一张幻灯片中解释发布和订阅:
要正确理解为什么需要多次调用find()
,您需要了解Meteor中集合、发布和订阅的工作方式:
You define collections in MongoDB. No Meteor involved yet. These collections contain database records (also called "documents" by both Mongo and Meteor, but a "document" is more general than a database record; for instance, an update specification or a query selector are documents too - JavaScript objects containing field: value
pairs).
Then you define collections on the Meteor server with
MyCollection = new Mongo.Collection('collection-name-in-mongo')
These collections contain all the data from the MongoDB collections, and you can run MyCollection.find({...})
on them, which will return a cursor (a set of records, with methods to iterate through them and return them).
This cursor is (most of the time) used to publish (send) a set of records (called a "record set"). You can optionally publish only some fields from those records. It is record sets (not collections) that clients subscribe to. Publishing is done by a publish function, which is called every time a new client subscribes, and which can take parameters to manage which records to return (e.g. a user id, to return only that user's documents).
On the client, you have Minimongo collections that partially mirror some of the records from the server. "Partially" because they may contain only some of the fields, and "some of the records" because you usually want to send to the client only the records it needs, to speed up page load, and only those it needs and has permission to access.
Minimongo is essentially an in-memory, non-persistent implementation of Mongo in pure JavaScript. It serves as a local cache that stores just the subset of the database that this client is working with. Queries on the client (find) are served directly out of this cache, without talking to the server.
These Minimongo collections are initially empty. They are filled by
Meteor.subscribe('record-set-name')
calls. Note that the parameter to subscribe isn't a collection name; it's the name of a record set that the server used in the publish
call. The subscribe()
call subscribes the client to a record set - a subset of records from the server collection (e.g. most recent 100 blog posts), with all or a subset of the fields in each record (e.g. only title
and date
). How does Minimongo know into which collection to place the incoming records? The name of the collection will be the collection
argument used in the publish handler's added
, changed
, and removed
callbacks, or if those are missing (which is the case most of the time), it will be the name of the MongoDB collection on the server.
修改记录
这是Meteor非常方便的地方:当您在客户端修改Minimongo集合中的记录(文档)时,Meteor会立即更新所有依赖它的模板,并将更改发送回服务器,服务器将把更改存储在MongoDB中,并将其发送到已订阅包含该文档的记录集的适当客户端。这被称为延迟补偿,是Meteor的七个核心原则之一。
多次订阅
您可以有一堆订阅来拉取不同的记录,但如果它们来自服务器上的同一集合并基于它们的_id
,它们最终都会进入客户端的同一个集合。这在Meteor文档中没有明确解释,但是暗示了这一点。
当您订阅记录集时,它会告诉服务器向客户端发送记录。客户端将这些记录存储在本地的Minimongo集合中,名称与发布处理程序中使用的
collection
参数相同,包括
added
,
changed
和
removed
回调。Meteor将排队接收的属性,直到您在客户端上声明具有匹配集合名称的Mongo.Collection。
未被解释的是,当您不显式使用
added
、
changed
和
removed
或根本不使用发布处理程序时会发生什么-这是大多数情况。在这种最常见的情况下,集合参数(毫不奇怪)取自您在第1步中在服务器上声明的MongoDB集合的名称。但是这意味着您可以具有不同名称的不同发布和订阅,并且所有记录都将在客户端的同一集合中结束。Meteor会在文档的
顶级字段层面执行集合并,以便订阅可以重叠-将不同顶级字段发送到客户端的发布函数并排工作,在客户端上,集合中的文档将是
两个字段集的并集。
示例:多个订阅填充客户端上的同一集合
您有一个BlogPosts集合,它在服务器和客户端上以相同的方式声明,即使它执行不同的操作:
BlogPosts = new Mongo.Collection('posts');
在客户端,
BlogPosts
可以从以下获取记录:
a subscription to the most recent 10 blog posts
Meteor.publish('posts-recent', function publishFunction() {
return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
}
Meteor.subscribe('posts-recent');
a subscription to the current user's posts
Meteor.publish('posts-current-user', function publishFunction() {
return BlogPosts.find({author: this.userId}, {sort: {date: -1}, limit: 10});
}
Meteor.publish('posts-by-user', function publishFunction(who) {
return BlogPosts.find({authorId: who._id}, {sort: {date: -1}, limit: 10});
}
Meteor.subscribe('posts-current-user');
Meteor.subscribe('posts-by-user', someUser);
a subscription to the most popular posts
- etc.
所有这些文档来自于 MongoDB 中的“posts”集合,通过服务器上的“BlogPosts”集合,并最终进入客户端中的“BlogPosts”集合。
现在我们可以理解为什么需要调用
find()
超过一次 - 第二次是在客户端进行,因为所有订阅的文档最终都会进入同一个集合,你只需要获取你关心的文档。例如,要在客户端获取最新的帖子,只需复制服务器上的查询即可:
var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});
这将返回客户端到目前为止已收到的所有文档/记录的光标,包括置顶帖和用户的帖子。(
感谢 Geoffrey)。
BlogPosts.find({})
会发生什么——它将返回当前在客户端上的所有文档/记录的光标,包括置顶帖子和用户帖子。我在SO上看到过其他一些问题,问者对此感到困惑。 - Geoffrey Booth