了解Meteor发布/订阅

时间:2013-11-07 02:19:06

标签: javascript mongodb meteor publish-subscribe

我有一个简单的应用设置,显示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()一次。

4 个答案:

答案 0 :(得分:282)

收藏,出版物和订阅是Meteor的一个棘手的领域,文档可以更详细地讨论,以避免frequent confusion,有时会被confusing terminology放大。

此处Sacha GreifDiscoverMeteor的合着者)在一张幻灯片中解释出版物和订阅:

subscriptions

要正确理解为什么需要多次调用find(),您需要了解集合,出版物和订阅在Meteor中的工作方式:

  1. 您可以在MongoDB中定义集合。没有流星参与了。这些集合包含 database records (也称为#34;文档&#34;由Mongo and Meteor和#34;文档&#34;更通用数据库记录;例如,更新规范或查询选择器是文档too - 包含field: value对的JavaScript对象。

  2. 然后使用

    在Meteor服务器上定义collections
    MyCollection = new Mongo.Collection('collection-name-in-mongo')
    

    这些集合包含来自MongoDB集合的全部数据,您可以对它们运行MyCollection.find({...}),这将返回 cursor (一组记录,包含迭代它们并返回它们的方法。

  3. 此光标(大部分时间)用于 publish (发送)一组记录(称为&#34;记录集&#34 ; )。您可以选择仅发布这些记录中的 some 字段。客户 subscribe 的记录集(集合)。发布由publish function完成,每次新客户端订阅时都会调用,并且可以使用参数来管理要返回的记录(例如,用户ID,仅返回该用户的文档)。

  4. 在客户端上,您有Minimongo个集合部分镜像部分来自服务器的记录。 &#34;部分&#34;因为它们可能只包含一些字段,而且还包含一些记录&#34;因为您通常只想向客户端发送所需的记录,以加快页面加载速度,只有需要访问的权限才能访问。

      

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

    这些Minimongo系列最初是空的。

    填补了它们
    Meteor.subscribe('record-set-name')
    

    呼叫。请注意,subscribe的参数不是集合名称;它是服务器在publish调用中使用的记录集的名称。 subscribe()调用将客户端订阅到记录集 - 来自服务器集合的记录子集(例如最近的100篇博文),每个记录中包含全部或部分字段(例如,只有titledate)。 Minimongo如何知道收集记录的集合?集合的名称将是发布处理程序的collectionaddedchanged回调中使用的removed参数,或者如果缺少这些参数(其中在大多数情况下是这种情况),它将是服务器上MongoDB集合的名称。

  5. 修改记录

    这就是Meteor让事情变得非常方便的地方:当您在客户端上修改Minimongo集合中的记录(文档)时,Meteor会立即更新依赖它的所有模板,并且还会将更改发送回服务器,反过来,它将存储MongoDB中的更改,并将它们发送到已订阅包含该文档的记录集的相应客户端。这称为延迟补偿,是seven core principles of Meteor

    之一

    多个订阅

    您可以拥有大量订阅来吸引不同的记录,但如果它们来自服务器上的同一个集合,那么它们最终会在客户端的同一个集合中,基于它们的{{1} }。这没有得到清楚的解释,但是由Meteor docs暗示:

      

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

    未解释的是当您明确使用removedaddedchanged或发布处理程序时会发生什么在所有 - 大部分时间。在这种最常见的情况下,收集参数(不出所料)取自您在步骤1中在服务器上声明的MongoDB集合的名称。但这意味着您可以使用不同的名称来创建不同的发布和订阅,并且所有记录将最终存储在客户端的同一集合中。低至顶级字段的级别,Meteor负责在文档之间执行集合联合,以便订阅可以重叠 - 将不同顶级字段发布到客户端工作的发布函数并排运行客户端,集合中的文档将是union of the two sets of fields

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

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

    removed

    在客户端上,BlogPosts = new Mongo.Collection('posts'); 可以从以下位置获取记录:

    1. 订阅最近的10篇博文

      BlogPosts
    2. 订阅当前用户的帖子

      // server
      Meteor.publish('posts-recent', function publishFunction() {
        return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
      }
      // client
      Meteor.subscribe('posts-recent');
      
    3. 订阅最受欢迎的帖子

    4. 所有这些文档都来自MongoDB中的// 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); 集合,通过服务器上的posts集合,最终位于客户端的BlogPosts集合中。

      现在我们可以理解为什么你需要多次调用BlogPosts - 第二次在客户端上,因为来自所有订阅的文档最终会在同一个集合中,并且你只需要获取那些你关心。例如,要在客户端上获取最新帖子,只需从服务器镜像查询:

      find()

      这会将光标返回到客户端目前收到的所有文档/记录,包括热门帖子和用户帖子。 (thanks Geoffrey)。

答案 1 :(得分:27)

是的,客户端find()仅返回Minimongo中客户端上的文档。来自docs

  

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

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

答案 2 :(得分:1)

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

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>

答案 3 :(得分:0)

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

    return Posts.find();

});

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