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

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

问题描述

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

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

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

 如果Meteor.isClient 
Template.projectsIndex.projects = Projects.find()

删除它后,我还要另外做:

 如果Meteor.isServer 
Meteor.publishprojects, - >
Projects.find()
如果Meteor.isClient
Meteor.subscribeprojects
Template.projectsIndex.projects = Projects.find()

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

解决方案

收藏,出版物和订阅是Meteor的一个棘手的领域,文档可以更详细地讨论,以避免 (也称为文档)通过Mongo 和Meteor ,但是 document比数据库记录更通用;例如,更新规范或查询选择器是文档也是 - 包含字段的JavaScript对象:值对)。


  • 然后定义< Meteor服务器上的href =http://docs.meteor.com/#collections =noreferrer>集合

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

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


  • 此光标(大部分时间)用于 发布 (发送)一组记录(称为记录集)。您可以选择仅从这些记录中发布 某些 字段。客户 订阅是记录集(集合)到。发布由发布功能完成,每次新客户订阅时都会调用该功能,并且可以执行用于管理要返回的记录的参数(例如,用户ID,仅返回该用户的文档)。


  • 在客户端,你有部分的 Minimongo 系列镜像来自服务器的一些记录。 部分因为它们可能只包含一些字段和一些记录,因为您通常只想向客户端发送所需的记录,以加快页面加载,并且只需要有权访问。


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


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



    填充

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

    来电。请注意,订阅的参数不是集合名称;它是服务器在发布调用中使用的记录集的名称。 subscribe()调用将客户端订阅到记录集 - 来自服务器集合的记录子集(例如最近的100篇博文),包含每个记录中的全部或部分字段(例如,仅标题日期)。 Minimongo如何知道收集记录的集合?集合的名称将是发布处理程序的添加中使用的集合参数,已更改已删除回调,或者如果缺少这些回调(大多数时候都是这种情况),它将是MongoDB集合的名称在服务器上。




  • 修改记录



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



    多个订阅



    您可以拥有大量订阅来吸引不同的记录,但他们会如果来自服务器上的相同集合,则所有这些都基于其 _id ,最终在客户端上的同一集合中。这没有得到清楚的解释,但Meteor docs暗示:


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


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



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



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

      BlogPosts = new Mongo.Collection('帖子); 

    在客户端上, 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由Meteor提供 - http://docs.meteor.com/#publish_userId
      }
      Meteor.publish('posts-by-user',function publishFunction(who){
      return BlogPosts.find({authorId:who._id},{sort:{date:-1} ,限制: 10});
      }

      //客户端
      Meteor.subscribe('posts-current-user');
      Meteor.subscribe('by-by-user',someUser);


    3. 订阅最受欢迎的帖子


    4. 等。

    所有这些文件来自帖子集合在MongoDB中,通过服务器上的 BlogPosts 集合,最终在客户端上的 BlogPosts 集合中。



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

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

    这会将光标返回到客户端到目前为止收到的所有文档/记录,两者都是顶部帖子和用户的帖子。 (感谢Geoffrey )。


    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天全站免登陆