了解流星发布/订阅

我已经设置了一个简单的应用程序来显示 Projects列表。我已经删除了 autopublish包,这样我就不会把所有东西都发送给客户端。

 <template name="projectsIndex">
{{#each projects}}
{{name}}
{{/each}}
</template>

autopublish打开时,这将显示所有项目:

if Meteor.isClient
Template.projectsIndex.projects = Projects.find()

除去它,我还要做:

 if Meteor.isServer
Meteor.publish "projects", ->
Projects.find()
if Meteor.isClient
Meteor.subscribe "projects"
Template.projectsIndex.projects = Projects.find()

那么,说客户端 find()方法只搜索从服务器端发布的记录是否准确?这让我很困惑,因为我觉得我应该只打一次 find()

24370 次浏览

是的,客户端 find ()只返回客户端在 Minimongo 的文档:

在客户机上,创建了一个 Minimongo 实例。Minimongo 本质上是一个内存中的、非持久化的 Mongo 实现,它使用的是纯 JavaScript。它充当本地缓存,仅存储此客户机正在使用的数据库子集。客户端上的查询(find)直接从这个缓存中提供,而不需要与服务器通信。

正如您所说的,publications ()指定客户机将拥有哪些文档。

集合、出版物和订阅是流星的一个棘手的领域,文档可以更详细地讨论,以避免 频繁 混乱,它有时会被 令人困惑的术语放大。

下面是 Sacha Greif(发现流星的合著者)用一张幻灯片解释出版物和订阅:

subscriptions

为了正确理解为什么需要多次调用 find(),您需要了解集合、出版物和订阅如何在“流星”中工作:

  1. 在 MongoDB 中定义集合。还没有陨石。这些集合包含 数据库记录(Mongo 和流星也称为“文档”,但“文档”比数据库记录更通用; 例如,更新规范或查询选择器是包含 field: value对的文档 也是-JavaScript 对象)。

  2. 然后定义 收藏品 在流星服务器上

    MyCollection = new Mongo.Collection('collection-name-in-mongo')
    

    这些集合包含来自 MongoDB 集合的数据 所有,您可以对它们运行 MyCollection.find({...}),它将返回一个 译自: 美国《科学》杂志网站(http://docs.mete.com/)(一组记录,带有方法来遍历它们并返回它们)。

  3. 这个游标(大多数情况下)用于 译自: 美国《科学》杂志网站(http://docs.mete.com/)(发送)一组记录(称为 “破纪录”)。您可以选择只发布这些记录中的 有些人认为这是一个错误的观点,他们认为这是一个错误的观点,他们认为这是一个错误的观点,他们认为这是一个错误的观点,他们认为这是一个错误的观点字段。客户机 译自: 美国《科学》杂志网站(http://docs.mete.com/)到的是记录集(没有集合)。发布是由 发布函数完成的,每次新客户端订阅时都会调用 发布函数,它可以使用参数来管理要返回的记录(例如用户 ID,只返回该用户的文档)。

  4. 在客户机 上,有 毫芒果集合,只是一部分镜像来自服务器的记录的 一些。“部分”是因为它们可能只包含一些字段,“一些记录”是因为您通常只想向客户端发送它所需要的记录,以加快页面加载,并且只有那些它需要 还有才有权访问。

    Minimongo 本质上是一个内存中的、非持久化的 Mongo 实现,它使用的是纯 JavaScript。它充当本地缓存,仅存储此客户机正在使用的数据库子集。客户端上的查询(find)直接从这个缓存中提供,而不需要与服务器通信。

    这些 Minimongo 集合最初是空的

    Meteor.subscribe('record-set-name')
    

    电话。请注意,订阅的参数不是集合名称; 它是服务器在 publish调用中使用的 破纪录了的名称。subscribe()调用将客户端订阅到一个 破纪录了-服务器集合中的一个记录子集(例如最近的100篇博客文章) ,每个记录中包含所有或一个字段子集(例如只包含 titledate)。Minimongo 如何知道将传入记录放入哪个集合?集合的名称将是发布处理程序的 addedchangedremoved回调中使用的 collection参数,或者如果缺少这些参数(这是大多数情况下的情况) ,它将是服务器上 MongoDB 集合的名称。

修改记录

这就是为什么在客户端上修改 Miniongo 集合中的记录(文档)时,Meetor 会立即更新所有依赖它的模板,并将更改发送回服务器,服务器将在 MongoDB 中存储更改,并将更改发送给订阅了包括该文档在内的记录集的适当客户端。这被称为 潜伏期补偿,是 流星的七个核心原理之一。

多次订阅

您可以有一大堆引入不同记录的订阅,但是如果这些订阅来自服务器上的同一个集合(基于它们的 _id) ,那么它们最终都会出现在客户机上的同一个集合中。这并没有得到明确的解释,但流星文档暗示了这一点:

当您订阅一个记录集时,它会告诉服务器将记录发送到客户端。客户机将这些记录存储在本地 Minimongo 集合中,其名称与发布处理程序的 addedchangedremoved回调中使用的 collection参数相同。陨石将队列传入的属性,直到您声明蒙戈。具有匹配的集合名称的客户端上的。

没有解释的是,当 不要显式地使用 addedchangedremoved,或者完全发布处理程序(大多数情况下)时会发生什么。在这种最常见的情况下,集合参数(不出所料)取自您在步骤1中在服务器上声明的 MongoDB 集合的名称。但是这意味着您可以使用不同的名称拥有不同的发布和订阅,并且所有记录都将最终出现在客户机上的同一集合中。在 顶级场地级别上,Meetor 注意在文档之间执行一个集合联合,这样订阅就可以重叠发布功能,将不同的顶级字段并排发布到客户端工作中,在客户端上,集合中的文档将是 两组字段的并

示例: 在客户端上填充同一集合的多个订阅

您有一个 BlogPosts 集合,它在服务器和客户机上以相同的方式声明,即使它做不同的事情:

BlogPosts = new Mongo.Collection('posts');

在客户端,BlogPosts可以从以下网站获取记录:

  1. 订阅最近的10篇博客文章

    // server
    Meteor.publish('posts-recent', function publishFunction() {
    return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
    }
    // client
    Meteor.subscribe('posts-recent');
    
  2. a subscription to the current user's posts

    // 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);
    
  3. a subscription to the most popular posts

  4. etc.

All these documents come from the posts collection in MongoDB, via the BlogPosts collection on the server, and end up in the BlogPosts collection on the client.

Now we can understand why you need to call find() more than once - the second time being on the client, because documents from all subscriptions will end up in the same collection, and you need to fetch only those you care about. For example, to get the most recent posts on the client, you simply mirror the query from the server:

var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});

这将返回一个光标到客户端到目前为止收到的所有文档/记录,包括顶部的帖子和用户的帖子。(谢谢 Geoffrey).

// on the server
Meteor.publish('posts', function() {


return Posts.find();


});


// on the client
Meteor.subscribe('posts');

这里的基本经验法则是 publishsubscribed变量名在客户端和服务器端应该是相同的。

MongoDB 和客户端上的集合名称应该相同。

假设我正在为名为 employees的集合使用发布和订阅,那么代码将如下所示


服务器端

在这里,var关键字的使用是可选的(使用此关键字使集合成为此文件的本地集合)。

CollectionNameOnServerSide = new Mongo.Collection('employees');


Meteor.publish('employeesPubSub', function() {
return CollectionNameOnServerSide.find({});
});

客户端. js 文件

CollectionNameOnClientSide = new Mongo.Collection('employees');
var employeesData = Meteor.subscribe('employeesPubSub');


Template.templateName.helpers({
'subcribedDataNotAvailable' : function(){
return !employeesData.ready();
},
'employeeNumbers' : () =>{
CollectionNameOnClientSide.find({'empId':1});
}
});

客户端. html 文件

在这里,我们可以使用 subcribedDataNotAvailable助手方法来知道客户端的数据是否准备好了,如果数据准备好了,然后使用 employeeNumbers助手方法打印员工号码。

<TEMPLATE name="templateName">
\{\{#if subcribedDataNotAvailable}}
<h1> data loading ... </h1>
\{\{else}}
\{\{#each employeeNumbers }}
\{\{this}}
\{\{/each}}
\{\{/if}}
<TEMPLATE>