了解meteor发布/订阅

我有一个简单的应用程序设置,显示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()方法只search从服务器端发布的logging是否准确呢? 它一直在绊倒我,因为我觉得我应该只调用find()一次。

收集,出版物和订阅是meteor的一个棘手的领域,文档可以更详细地讨论,以避免频繁的 混淆 ,有时通过混淆术语而被放大。

这里是Sacha Greif ( DiscoverMeteor的合着者)在一张幻灯片中解释出版物和订阅:

订阅

要正确理解为什么需要多次调用find() ,您需要了解如何在Meteor中使用集合,发布和订阅:

  1. 你在MongoDB中定义集合。 没有Meteor涉及。 这些集合包含数据库logging (也被Mongo 和Meteor称为“文档”,但是“文档”比数据库logging更普遍;例如,更新规范或查询select器也是文档 – 包含field: value JavaScript对象对)。

  2. 然后你用Meteor服务器定义集合

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

    这些集合包含来自MongoDB集合的所有数据,并且可以在它们上运行MyCollection.find({...}) ,这将返回一个游标 (一组logging,带有遍历它们并返回它们的方法)。

  3. 这个游标(大部分时间)用于发布 (发送)一组logging(称为“logging集” )。 您可以select只从这些logging发布一些字段。 它是客户订阅的logging集( 而不是集合)。 发布是通过一个发布函数来完成的,每次新客户订阅时都会调用它,并且可以通过参数来pipe理要返回的logging(例如,用户标识,仅返回该用户的文档)。

  4. 在客户端 ,你有Minimongo集合, 部分镜像服务器的一些logging。 “部分”,因为它们可能只包含一些字段和“一些logging”,因为通常只需要向客户端发送所需的logging,加快页面加载速度,并且只有那些需要并且有权限访问。

    Minimongo本质上是纯JavaScript中的内存中非持久化的Mongo实现。 它充当本地caching,仅存储该客户端正在使用的数据库的子集。 客户端上的查询(查找)直接从caching中提供,而不与服务器通话。

    这些Minimongo集合最初是空的。 他们充满了

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

    调用。 请注意, 订阅的参数不是集合名称; 它是在publish调用中使用的服务器的logging集的名称。 subscribe()调用将客户端订阅到logging集 – 服务器集合中的logging子集(例如最近的100篇博客文章),每个logging中的全部或部分字段(例如只有titledate )。 Minimongo如何知道哪个collections放置传入的logging? 集合的名称将是发布处理程序的addedchangedremovedcallback中使用的collection参数,或者如果缺less这些参数(大多数情况下是这种情况),则它将成为该集合上的MongoDB集合的名称服务器。

修改logging

这就是meteor使事情变得非常方便的地方:当你在客户端的Minimongo集合中修改一个logging(文档)时,Meteor会立即更新所有依赖它的模板,并且将这些变化发送回服务器。会将更改存储在MongoDB中,并将它们发送给订阅包含该文档的logging集的相应客户端。 这就是所谓的延迟补偿 ,是meteor七大核心原理之一 。

多订阅

你可以有一大堆订阅拉入不同的logging,但是如果它们来自服务器上的同一个集合,那么它们将全部以客户端的相同集合为基础。 这个没有解释清楚,但meteor文件暗示:

当您订阅logging集时,它会通知服务器将logging发送到客户端。 客户端将这些logging存储在本地Minimongo集合中,与发布处理程序的addedchangedremovedcallback中使用的collection参数名称相同。 Meteor将对传入的属性进行排队,直到在客户端上使用匹配的集合名称声明Mongo.Collection。

没有解释的是,当你明确地使用addedchangedremoved或者发布处理程序的时候会发生什么 – 这是大部分时间。 在这种最常见的情况下,收集参数(不出意外)取自您在步骤1在服务器上声明的MongoDB集合的名称。但这意味着您可以使用不同的名称进行不同的发布和订阅,logging将最终在客户端的同一个集合中。 一直到顶层字段的层次 ,meteor注意在文档之间执行一个集合,使得订阅可以重叠 – 发布将不同顶层字段发送到客户端并在客户端上的函数,收集将是两套领域的结合 。

例如:多个订阅在客户端填充相同的集合

你有一个BlogPosts集合,你在服务器和客户端都声明同样的方法,即使它做了不同的事情:

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

在客户端上, BlogPosts可以从以下位置获取logging:

  1. 订阅最近的10篇博文

     // server Meteor.publish('posts-recent', function publishFunction() { return BlogPosts.find({}, {sort: {date: -1}, limit: 10}); } // client Meteor.subscribe('posts-recent'); 
  2. 订阅当前用户的post

     // 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. 订阅最热门的post

  4. 等等

所有这些文档都来自MongoDB中的posts集合,通过服务器上的BlogPosts集合,最后到达客户端的BlogPosts集合。

现在我们可以理解为什么需要多次调用find() – 第二次是在客户端上,因为来自所有订阅的文档将在同一个集合中结束,并且只需要获取您关心的那些。 例如,要获取客户端上的最新post,只需从服务器镜像查询即可:

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

这将返回一个光标到客户端迄今为止收到的所有文档/logging,包括顶端文章和用户post。 ( 谢谢杰弗里 )。

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

在客户端上创build一个Minimongo实例。 Minimongo本质上是纯JavaScript中的内存中非持久化的Mongo实现。 它充当本地caching,仅存储该客户端正在使用的数据库的子集。 客户端上的查询(查找)直接从caching中提供,而不与服务器通话。

正如你所说,发布()指定客户端将拥有哪些文件。

 // on the server Meteor.publish('posts', function() { return Posts.find(); }); // on the client Meteor.subscribe('posts'); 

这里的基本拇指规则是publishsubscribedvariables名称应该在客户端和服务器端相同。

Mongo DB和客户端的集合名称应该相同。

假设我正在使用发布和订阅名为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>