了解 Meteor 发布/订阅 [英] Understanding Meteor Publish / Subscribe

查看:35
本文介绍了了解 Meteor 发布/订阅的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的应用程序设置,它显示了一个 项目 列表.我已经删除了 autopublish 包,这样我就不会向客户端发送所有内容.

 <模板名称="projectsIndex">{{#每个项目}}{{姓名}}{{/每个}}

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

if Meteor.isClientTemplate.projectsIndex.projects = Projects.find()

删除后,我还必须做:

 如果 Meteor.isServerMeteor.publish项目",->项目.find()如果 Meteor.isClientMeteor.subscribe项目"Template.projectsIndex.projects = Projects.find()

那么,说客户端find()方法只搜索服务器端发布的记录是否准确?它一直让我感到困惑,因为我觉得我应该只调用一次 find().

解决方案

收藏、发布和订阅是 Meteor 的一个棘手领域,文档可以更详细地讨论,以避免 (也称为文档"来自 Mongo 和 Meteor,但是文档"比数据库记录更通用;例如,更新规范或查询选择器是文档 也是 - 包含 field: value 对的 JavaScript 对象.

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

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

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

  • 这个光标(大部分时间)用于发布(发送)一组记录(称为记录集").您可以选择仅发布这些记录中的一些字段.客户订阅<的是记录集(不是集合)/strong> 到.发布是由 发布函数完成的,每次有新客户端订阅时都会调用它,并且可以用于管理要返回哪些记录的参数(例如,用户 ID,仅返回该用户的文档).

  • 在客户端,您有 Minimongo 集合,部分 镜像来自服务器的部分 记录.部分"是因为它们可能只包含部分字段,而部分记录"是因为您通常只想向客户端发送它需要的记录,以加快页面加载速度,并且只发送它需要的记录 有访问权限.

    <块引用>

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

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

    填充

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

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

  • 修改记录

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

    多个订阅

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

    <块引用>

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

    没有解释的是当你明确使用 addedchangedremoved时会发生什么,或者完全发布处理程序 - 大多数情况下是这样.在这种最常见的情况下,集合参数(不出所料)取自您在第 1 步在服务器上声明的 MongoDB 集合的名称.但这意味着您可以拥有不同名称的不同发布和订阅,以及所有记录将最终出现在客户端的同一个集合中.在顶级字段级别,Meteor 负责在文档之间执行集合联合,以便订阅可以重叠 - 发布将不同顶级字段发送到客户端的功能并排工作客户端,集合中的文档将是两组字段的联合.

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

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

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

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

    1. 订阅最近的 10 篇博文

      //服务器Meteor.publish('posts-recent', function publishFunction() {return BlogPosts.find({}, {sort: {date: -1}, limit: 10});}//客户Meteor.subscribe('posts-recent');

    2. 订阅当前用户的帖子

      //服务器Meteor.publish('posts-current-user', function publishFunction() {return BlogPosts.find({author: this.userId}, {sort: {date: -1}, limit: 10});//this.userId 由 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});}//客户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});

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

    I've got a simple app set up that shows a list of Projects. I've removed the autopublish package so that I'm not sending everything to the client.

     <template name="projectsIndex">    
       {{#each projects}}      
         {{name}}
       {{/each}}
     </template>
    

    When autopublish was turned on, this would display all the projects:

    if Meteor.isClient
      Template.projectsIndex.projects = Projects.find()
    

    With it removed, I have to additionally do:

     if Meteor.isServer
       Meteor.publish "projects", ->
         Projects.find()
     if Meteor.isClient
       Meteor.subscribe "projects"
       Template.projectsIndex.projects = Projects.find()
    

    So, is it accurate to say that the client-side find() method only searches records which have been published from the server-side? It's been tripping me up because I felt like I should only be calling find() once.

    解决方案

    Collections, publications and subscriptions are a tricky area of Meteor, that the documentation could discuss in more detail, so as to avoid frequent confusion, which sometimes get amplified by confusing terminology.

    Here's Sacha Greif (co-author of DiscoverMeteor) explaining publications and subscriptions in one slide:

    To properly understand why you need to call find() more than once, you need to understand how collections, publications and subscriptions work in Meteor:

    1. You define collections in MongoDB. No Meteor involved yet. These collections contain database records (also called "documents" by both Mongo and Meteor, but a "document" is more general than a database record; for instance, an update specification or a query selector are documents too - JavaScript objects containing field: value pairs).

    2. Then you define collections on the Meteor server with

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

      These collections contain all the data from the MongoDB collections, and you can run MyCollection.find({...}) on them, which will return a cursor (a set of records, with methods to iterate through them and return them).

    3. This cursor is (most of the time) used to publish (send) a set of records (called a "record set"). You can optionally publish only some fields from those records. It is record sets (not collections) that clients subscribe to. Publishing is done by a publish function, which is called every time a new client subscribes, and which can take parameters to manage which records to return (e.g. a user id, to return only that user's documents).

    4. On the client, you have Minimongo collections that partially mirror some of the records from the server. "Partially" because they may contain only some of the fields, and "some of the records" because you usually want to send to the client only the records it needs, to speed up page load, and only those it needs and has permission to access.

      Minimongo is essentially an in-memory, non-persistent implementation of Mongo in pure JavaScript. It serves as a local cache that stores just the subset of the database that this client is working with. Queries on the client (find) are served directly out of this cache, without talking to the server.

      These Minimongo collections are initially empty. They are filled by

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

      calls. Note that the parameter to subscribe isn't a collection name; it's the name of a record set that the server used in the publish call. The subscribe() call subscribes the client to a record set - a subset of records from the server collection (e.g. most recent 100 blog posts), with all or a subset of the fields in each record (e.g. only title and date). How does Minimongo know into which collection to place the incoming records? The name of the collection will be the collection argument used in the publish handler's added, changed, and removed callbacks, or if those are missing (which is the case most of the time), it will be the name of the MongoDB collection on the server.

    Modifying records

    This is where Meteor makes things very convenient: when you modify a record (document) in the Minimongo collection on the client, Meteor will instantly update all templates that depend on it, and will also send the changes back to the server, which in turn will store the changes in MongoDB and will send them to the appropriate clients that have subscribed to a record set including that document. This is called latency compensation and is one of the seven core principles of Meteor.

    Multiple subscriptions

    You can have a bunch of subscriptions that pull in different records, but they'll all end up in the same collection on the client if the came from the same collection on the server, based on their _id. This is not explained clearly, but implied by the Meteor docs:

    When you subscribe to a record set, it tells the server to send records to the client. The client stores these records in local Minimongo collections, with the same name as the collection argument used in the publish handler's added, changed, and removed callbacks. Meteor will queue incoming attributes until you declare the Mongo.Collection on the client with the matching collection name.

    What's not explained is what happens when you don't explicitly use added, changed and removed, or publish handlers at all - which is most of the time. In this most common case, the collection argument is (unsurprisingly) taken from the name of the MongoDB collection you declared on the server at step 1. But what this means is that you can have different publications and subscriptions with different names, and all the records will end up in the same collection on the client. Down to the level of top level fields, Meteor takes care to perform a set union among documents, such that subscriptions can overlap - publish functions that ship different top level fields to the client work side by side and on the client, the document in the collection will be the union of the two sets of fields.

    Example: multiple subscriptions filling the same collection on the client

    You have a BlogPosts collection, which you declare the same way on both the server and the client, even though it does different things:

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

    On the client, BlogPosts can get records from:

    1. a subscription to the most recent 10 blog posts

      // server
      Meteor.publish('posts-recent', function publishFunction() {
        return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
      }
      // client
      Meteor.subscribe('posts-recent');
      

    2. a subscription to the current user's posts

      // 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. a subscription to the most popular posts

    4. etc.

    All these documents come from the posts collection in MongoDB, via the BlogPosts collection on the server, and end up in the BlogPosts collection on the client.

    Now we can understand why you need to call find() more than once - the second time being on the client, because documents from all subscriptions will end up in the same collection, and you need to fetch only those you care about. For example, to get the most recent posts on the client, you simply mirror the query from the server:

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

    This will return a cursor to all documents/records that the client has received so far, both the top posts and the user's posts. (thanks Geoffrey).

    这篇关于了解 Meteor 发布/订阅的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆