了解Meteor发布/订阅

DVG*_*DVG 84 javascript publish-subscribe mongodb meteor

我有一个简单的应用程序设置,显示一个列表Projects.我已经删除了autopublish包,所以我不会将所有内容发送给客户端.

 <template name="projectsIndex">    
   {{#each projects}}      
     {{name}}
   {{/each}}
 </template>
Run Code Online (Sandbox Code Playgroud)

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

if Meteor.isClient
  Template.projectsIndex.projects = Projects.find()
Run Code Online (Sandbox Code Playgroud)

删除后,我还要另外做:

 if Meteor.isServer
   Meteor.publish "projects", ->
     Projects.find()
 if Meteor.isClient
   Meteor.subscribe "projects"
   Template.projectsIndex.projects = Projects.find()
Run Code Online (Sandbox Code Playgroud)

那么,说客户端find()方法只搜索从服务器端发布的记录是否准确?它一直在绊倒我,因为我觉得我应该只打电话find()一次.

Dan*_*scu 285

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

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

订阅

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

  1. 您在MongoDB中定义集合.没有流星参与了.这些集合包含数据库记录(也称为"文件"并举蒙戈和流星,而是一个"文档"是不是数据库记录更普遍的;例如,更新规范或查询选择的文件太多 -包含JavaScript对象field: value对).

  2. 然后定义集合 流星服务器上使用

    MyCollection = new Mongo.Collection('collection-name-in-mongo')
    
    Run Code Online (Sandbox Code Playgroud)

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

  3. 该光标(大部分时间)用于发布(发送)一组记录(称为"记录集").您可以选择仅发布这些记录中的某些字段.它是客户订阅的记录集(不是集合).发布由发布功能完成,发布功能在每次新客户端订阅时调用,并且可以采用参数来管理要返回的记录(例如,用户ID,仅返回该用户的文档).

  4. 在客户端上,您有Minimongo集合,它们部分镜像服务器中的某些记录."部分"因为它们可能只包含一些字段和"一些记录",因为您通常只想向客户端发送所需的记录,以加快页面加载,并且只需要它们需要的权限并且有权使用访问.

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

    这些Minimongo系列最初是空的.他们充满了

    Meteor.subscribe('record-set-name')
    
    Run Code Online (Sandbox Code Playgroud)

    调用.请注意,要订阅的参数不是集合名称; 它是服务器在调用中使用的记录集的名称publish.该subscribe()调用将客户端订阅到记录集 - 来自服务器集合的记录的子集(例如,最近的100个博客帖子),每个记录中的所有或部分字段(例如,仅titledate).Minimongo如何知道收集记录的集合?集合的名称将collection在发布处理程序的使用参数added,changedremoved回调,或者如果这些丢失(这是大多数时间的情况下),这将是服务器上的MongoDB集合的名称.

修改记录

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

多个订阅

您可以拥有大量订阅来吸引不同的记录,但如果它们来自服务器上的同一个集合,它们将最终出现在客户端的同一个集合中,基于它们_id.这没有得到清楚的解释,但Meteor文档暗示:

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

没有解释的是当你没有明确地使用added,changedremoved/或发布处理程序时会发生什么- 大多数情况下.在这种最常见的情况下,收集参数(不出所料)取自您在步骤1中在服务器上声明的MongoDB集合的名称.但这意味着您可以使用不同的名称来创建不同的发布和订阅,以及所有记录将最终存储在客户端的同一集合中.降低到顶级字段的级别,Meteor负责在文档之间执行集合联合,这样订阅可以重叠 - 发布将不同顶级字段发送到客户端并在客户端上工作的函数,该文档位于集合将是两组领域的结合.

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

您有一个BlogPosts集合,您可以在服务器和客户端上以相同的方式声明它,即使它执行不同的操作:

BlogPosts = new Mongo.Collection('posts');
Run Code Online (Sandbox Code Playgroud)

在客户端,BlogPosts可以从以下位置获取记录:

  1. 订阅最近的10篇博文

    // server
    Meteor.publish('posts-recent', function publishFunction() {
      return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
    }
    // client
    Meteor.subscribe('posts-recent');
    
    Run Code Online (Sandbox Code Playgroud)
  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);
    
    Run Code Online (Sandbox Code Playgroud)
  3. 订阅最受欢迎的帖子

  4. 等等

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

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

var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});
Run Code Online (Sandbox Code Playgroud)

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

  • 这很棒.也许值得一提的是,如果你在订阅这两个出版物后在客户端上执行`BlogPosts.find({})`会发生什么 - 即它会返回当前客户端上所有文档/记录的光标,包括顶部帖子和用户的帖子.我在SO上看到了其他问题,提问者对此感到困惑. (10认同)
  • 是否可以只调用您订阅的记录集?在中,是否可以直接获取我的javascript中的记录集,而不是在本地查询Minimongo数据库? (4认同)
  • 这很棒.谢谢.此外,Meteor.users()集合在客户端自动发布时有点令人困惑.可以在上面的答案中添加一点来说明users()集合吗? (3认同)
  • 即使比最初要求的要多得多,我认为@DVG应该将这篇伟大的文章标记为已接受的答案.谢谢丹. (3认同)

use*_*291 27

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

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

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