84

我有一个简单的应用程序设置,显示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

4 回答 4

287

收藏、出版物和订阅是 Meteor 的一个棘手领域,文档可以更详细地讨论,以避免经常 混淆,有时会被混淆的术语放大。

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

订阅

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

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

  2. 然后在 Meteor 服务器上定义集合

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

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

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

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

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

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

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

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

修改记录

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

多个订阅

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

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

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

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

您有一个 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. 订阅最受欢迎的帖子

  4. 等等

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

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

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

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

于 2014-02-18T12:07:33.660 回答
27

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

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

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

于 2013-11-07T02:28:08.747 回答
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>
于 2017-02-27T09:47:28.870 回答
0
// on the server
Meteor.publish('posts', function() {

    return Posts.find();

});

// on the client
Meteor.subscribe('posts');
于 2017-01-27T13:59:36.590 回答