【问题标题】:Understanding Meteor Publish / Subscribe了解 Meteor 发布/订阅
【发布时间】:2013-11-18 13:25:08
【问题描述】:

我设置了一个简单的应用程序,显示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() 打一次电话。

【问题讨论】:

    标签: javascript mongodb meteor publish-subscribe


    【解决方案1】:

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

    这是Sacha GreifDiscoverMeteor 的合著者)在一张幻灯片中解释出版物和订阅:

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

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

    2. 然后你在 Meteor 服务器上定义collections

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

      这些集合包含所有来自 MongoDB 集合的数据,您可以在它们上运行 MyCollection.find({...}),这将返回 cursor(一组记录, 以及遍历它们并返回它们的方法。

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

      李>
    4. 在客户端,您有Minimongo 集合,这些集合部分反映了一些来自服务器的记录。 “部分”是因为它们可能只包含一些字段,而“一些记录”是因为您通常只想将它需要的记录发送给客户端,以加快页面加载速度,并且只发送它需要的那些 有访问权限。

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

      这些 Minimongo 集合最初是空的。它们由

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

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

    修改记录

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

    多个订阅

    您可以拥有一堆拉入不同记录的订阅,但如果来自服务器上的同一个集合,它们最终都会在客户端的同一个集合中,基于他们的_id。这没有解释清楚,但 Meteor 文档暗示了这一点:

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

    没有解释的是,当您明确使用addedchangedchanged 或根本不发布处理程序时会发生什么 - 大多数情况下。在这种最常见的情况下,collection 参数(不出所料)取自您在步骤 1 中在服务器上声明的 MongoDB 集合的名称。但这意味着您可以拥有不同名称的不同发布和订阅,并且所有记录将在客户端的同一集合中结束。向下到顶级字段的级别,Meteor 负责在文档之间执行集合并集,以便订阅可以重叠 - 发布功能,将不同的顶级字段并排传送到客户端工作客户端,集合中的文档将是union of the two sets of fields

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

    您有一个 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. 订阅当前用户的帖子

      // 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. 订阅最受欢迎的帖子

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

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

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

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

    【讨论】:

    • 这很棒。也许值得一提的是,如果您在订阅了这两个出版物后在客户端上执行BlogPosts.find({}) 会发生什么——即它将返回当前在客户端上的所有文档/记录的光标,包括顶部帖子和用户的帖子。我在 SO 上看到其他问题,提问者对此感到困惑。
    • 这很棒。谢谢。此外,Meteor.users() 集合有点令人困惑,因为它是在客户端自动发布的。可以在上面的答案中添加一点来说明 users() 集合吗?
    • 即使比最初要求的要多得多,我认为@DVG 应该将这篇出色的文章标记为已接受的答案。谢谢丹。
    • 谢谢@DanDascalescu,很好的解释让我明白了很多,在阅读您的解释后关注关于“收藏”的流星文档时,我认为BlogPosts 不是收藏,它是返回的具有“插入”、“更新”等方法的对象,客户端和服务器中的真实集合是posts
    • 是否可以只调用您订阅的记录集?如中,是否可以直接在我的javascript中获取记录集,而不是在本地查询Minimongo db?
    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

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

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

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


      服务器端

      这里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 helper 方法来了解客户端数据是否准备好,如果数据准备好则使用employeeNumbers helper 方法打印员工编号。

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

      【讨论】:

        【解决方案4】:
        // on the server
        Meteor.publish('posts', function() {
        
            return Posts.find();
        
        });
        
        // on the client
        Meteor.subscribe('posts');
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-11-29
          • 2017-11-30
          • 2016-03-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多