了解meteor发布/订阅
我有一个简单的应用程序设置,显示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()
方法只search从服务器端发布的logging是否准确呢? 它一直在绊倒我,因为我觉得我应该只调用find()
一次。
收集,出版物和订阅是meteor的一个棘手的领域,文档可以更详细地讨论,以避免频繁的 混淆 ,有时通过混淆术语而被放大。
这里是Sacha Greif ( DiscoverMeteor的合着者)在一张幻灯片中解释出版物和订阅:
要正确理解为什么需要多次调用find()
,您需要了解如何在Meteor中使用集合,发布和订阅:
-
你在MongoDB中定义集合。 没有Meteor涉及。 这些集合包含数据库logging (也被Mongo 和Meteor称为“文档”,但是“文档”比数据库logging更普遍;例如,更新规范或查询select器也是文档 – 包含
field: value
JavaScript对象对)。 -
然后你用Meteor服务器定义集合
MyCollection = new Mongo.Collection('collection-name-in-mongo')
这些集合包含来自MongoDB集合的所有数据,并且可以在它们上运行
MyCollection.find({...})
,这将返回一个游标 (一组logging,带有遍历它们并返回它们的方法)。 -
这个游标(大部分时间)用于发布 (发送)一组logging(称为“logging集” )。 您可以select只从这些logging发布一些字段。 它是客户订阅的logging集( 而不是集合)。 发布是通过一个发布函数来完成的,每次新客户订阅时都会调用它,并且可以通过参数来pipe理要返回的logging(例如,用户标识,仅返回该用户的文档)。
-
在客户端 ,你有Minimongo集合, 部分镜像服务器的一些logging。 “部分”,因为它们可能只包含一些字段和“一些logging”,因为通常只需要向客户端发送所需的logging,加快页面加载速度,并且只有那些需要并且有权限访问。
Minimongo本质上是纯JavaScript中的内存中非持久化的Mongo实现。 它充当本地caching,仅存储该客户端正在使用的数据库的子集。 客户端上的查询(查找)直接从caching中提供,而不与服务器通话。
这些Minimongo集合最初是空的。 他们充满了
Meteor.subscribe('record-set-name')
调用。 请注意, 订阅的参数不是集合名称; 它是在
publish
调用中使用的服务器的logging集的名称。subscribe()
调用将客户端订阅到logging集 – 服务器集合中的logging子集(例如最近的100篇博客文章),每个logging中的全部或部分字段(例如只有title
和date
)。 Minimongo如何知道哪个collections放置传入的logging? 集合的名称将是发布处理程序的added
,changed
和removed
callback中使用的collection
参数,或者如果缺less这些参数(大多数情况下是这种情况),则它将成为该集合上的MongoDB集合的名称服务器。
修改logging
这就是meteor使事情变得非常方便的地方:当你在客户端的Minimongo集合中修改一个logging(文档)时,Meteor会立即更新所有依赖它的模板,并且将这些变化发送回服务器。会将更改存储在MongoDB中,并将它们发送给订阅包含该文档的logging集的相应客户端。 这就是所谓的延迟补偿 ,是meteor七大核心原理之一 。
多订阅
你可以有一大堆订阅拉入不同的logging,但是如果它们来自服务器上的同一个集合,那么它们将全部以客户端的相同集合为基础。 这个没有解释清楚,但meteor文件暗示:
当您订阅logging集时,它会通知服务器将logging发送到客户端。 客户端将这些logging存储在本地Minimongo集合中,与发布处理程序的
added
,changed
和removed
callback中使用的collection
参数名称相同。 Meteor将对传入的属性进行排队,直到在客户端上使用匹配的集合名称声明Mongo.Collection。
没有解释的是,当你不明确地使用added
, changed
, removed
或者发布处理程序的时候会发生什么 – 这是大部分时间。 在这种最常见的情况下,收集参数(不出意外)取自您在步骤1在服务器上声明的MongoDB集合的名称。但这意味着您可以使用不同的名称进行不同的发布和订阅,logging将最终在客户端的同一个集合中。 一直到顶层字段的层次 ,meteor注意在文档之间执行一个集合,使得订阅可以重叠 – 发布将不同顶层字段发送到客户端并在客户端上的函数,收集将是两套领域的结合 。
例如:多个订阅在客户端填充相同的集合
你有一个BlogPosts集合,你在服务器和客户端都声明同样的方法,即使它做了不同的事情:
BlogPosts = new Mongo.Collection('posts');
在客户端上, BlogPosts
可以从以下位置获取logging:
-
订阅最近的10篇博文
// server Meteor.publish('posts-recent', function publishFunction() { return BlogPosts.find({}, {sort: {date: -1}, limit: 10}); } // client Meteor.subscribe('posts-recent');
-
订阅当前用户的post
// 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);
-
订阅最热门的post
- 等等
所有这些文档都来自MongoDB中的posts
集合,通过服务器上的BlogPosts
集合,最后到达客户端的BlogPosts
集合。
现在我们可以理解为什么需要多次调用find()
– 第二次是在客户端上,因为来自所有订阅的文档将在同一个集合中结束,并且只需要获取您关心的那些。 例如,要获取客户端上的最新post,只需从服务器镜像查询即可:
var recentPosts = BlogPosts.find({}, {sort: {date: -1}, limit: 10});
这将返回一个光标到客户端迄今为止收到的所有文档/logging,包括顶端文章和用户post。 ( 谢谢杰弗里 )。
是的,客户端find()只返回Minimongo客户端的文档。 从文档 :
在客户端上创build一个Minimongo实例。 Minimongo本质上是纯JavaScript中的内存中非持久化的Mongo实现。 它充当本地caching,仅存储该客户端正在使用的数据库的子集。 客户端上的查询(查找)直接从caching中提供,而不与服务器通话。
正如你所说,发布()指定客户端将拥有哪些文件。
// on the server Meteor.publish('posts', function() { return Posts.find(); }); // on the client Meteor.subscribe('posts');
这里的基本拇指规则是publish
和subscribed
variables名称应该在客户端和服务器端相同。
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>