我有一个简单的应用设置,显示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()
一次。
答案 0 :(得分:282)
收藏,出版物和订阅是Meteor的一个棘手的领域,文档可以更详细地讨论,以避免frequent confusion,有时会被confusing terminology放大。
此处Sacha Greif(DiscoverMeteor的合着者)在一张幻灯片中解释出版物和订阅:
要正确理解为什么需要多次调用find()
,您需要了解集合,出版物和订阅在Meteor中的工作方式:
您可以在MongoDB中定义集合。没有流星参与了。这些集合包含 database records (也称为#34;文档&#34;由Mongo and Meteor和#34;文档&#34;更通用数据库记录;例如,更新规范或查询选择器是文档too - 包含field: value
对的JavaScript对象。
然后使用
在Meteor服务器上定义collectionsMyCollection = new Mongo.Collection('collection-name-in-mongo')
这些集合包含来自MongoDB集合的全部数据,您可以对它们运行MyCollection.find({...})
,这将返回 cursor (一组记录,包含迭代它们并返回它们的方法。
此光标(大部分时间)用于 publish (发送)一组记录(称为&#34;记录集&#34 ; 强>)。您可以选择仅发布这些记录中的 some 字段。客户 subscribe 的记录集(不集合)。发布由publish function完成,每次新客户端订阅时都会调用,并且可以使用参数来管理要返回的记录(例如,用户ID,仅返回该用户的文档)。
在客户端上,您有Minimongo个集合部分镜像部分来自服务器的记录。 &#34;部分&#34;因为它们可能只包含一些字段,而且还包含一些记录&#34;因为您通常只想向客户端发送所需的记录,以加快页面加载速度,只有和需要访问的权限才能访问。
Minimongo本质上是纯JavaScript中Mongo的内存中非持久性实现。它充当本地缓存,仅存储此客户端正在使用的数据库的子集。客户端(查找)上的查询直接从此缓存中提供,而无需与服务器通信。
这些Minimongo系列最初是空的。
填补了它们Meteor.subscribe('record-set-name')
呼叫。请注意,subscribe的参数不是集合名称;它是服务器在publish
调用中使用的记录集的名称。 subscribe()
调用将客户端订阅到记录集 - 来自服务器集合的记录子集(例如最近的100篇博文),每个记录中包含全部或部分字段(例如,只有title
和date
)。 Minimongo如何知道收集记录的集合?集合的名称将是发布处理程序的collection
,added
和changed
回调中使用的removed
参数,或者如果缺少这些参数(其中在大多数情况下是这种情况),它将是服务器上MongoDB集合的名称。
这就是Meteor让事情变得非常方便的地方:当您在客户端上修改Minimongo集合中的记录(文档)时,Meteor会立即更新依赖它的所有模板,并且还会将更改发送回服务器,反过来,它将存储MongoDB中的更改,并将它们发送到已订阅包含该文档的记录集的相应客户端。这称为延迟补偿,是seven core principles of Meteor。
之一您可以拥有大量订阅来吸引不同的记录,但如果它们来自服务器上的同一个集合,那么它们最终会在客户端的同一个集合中,基于它们的{{1} }。这没有得到清楚的解释,但是由Meteor docs暗示:
订阅记录集时,它会告诉服务器将记录发送到客户端。客户端将这些记录存储在本地Minimongo集合中,其名称与发布处理程序
_id
,collection
和added
回调中使用的changed
参数相同。 Meteor将对传入属性进行排队,直到您在客户端上使用匹配的集合名称声明Mongo.Collection。
未解释的是当您不明确使用removed
,added
和changed
或发布处理程序时会发生什么在所有 - 大部分时间。在这种最常见的情况下,收集参数(不出所料)取自您在步骤1中在服务器上声明的MongoDB集合的名称。但这意味着您可以使用不同的名称来创建不同的发布和订阅,并且所有记录将最终存储在客户端的同一集合中。低至顶级字段的级别,Meteor负责在文档之间执行集合联合,以便订阅可以重叠 - 将不同顶级字段发布到客户端工作的发布函数并排运行客户端,集合中的文档将是union of the two sets of fields。
你有一个BlogPosts集合,你在服务器和客户端上都声明了相同的方式,即使它做了不同的事情:
removed
在客户端上,BlogPosts = new Mongo.Collection('posts');
可以从以下位置获取记录:
订阅最近的10篇博文
BlogPosts
订阅当前用户的帖子
// server
Meteor.publish('posts-recent', function publishFunction() {
return BlogPosts.find({}, {sort: {date: -1}, limit: 10});
}
// client
Meteor.subscribe('posts-recent');
订阅最受欢迎的帖子
所有这些文档都来自MongoDB中的// 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);
集合,通过服务器上的posts
集合,最终位于客户端的BlogPosts
集合中。
现在我们可以理解为什么你需要多次调用BlogPosts
- 第二次在客户端上,因为来自所有订阅的文档最终会在同一个集合中,并且你只需要获取那些你关心。例如,要在客户端上获取最新帖子,只需从服务器镜像查询:
find()
这会将光标返回到客户端目前收到的所有文档/记录,包括热门帖子和用户帖子。 (thanks Geoffrey)。
答案 1 :(得分:27)
是的,客户端find()仅返回Minimongo中客户端上的文档。来自docs:
在客户端上,创建一个Minimongo实例。 Minimongo本质上是纯JavaScript中Mongo的内存中非持久性实现。它充当本地缓存,仅存储此客户端正在使用的数据库的子集。客户端(查找)上的查询直接从此缓存中提供,而无需与服务器通信。
正如您所说,publish()指定客户端将拥有哪些文档。
答案 2 :(得分:1)
这里的基本拇指规则是publish
,subscribed
变量名在客户端和服务器端应该相同。
Mongo DB和客户端的集合名称应该相同。
假设我正在使用发布和订阅我的名为employees
的集合,那么代码看起来像
此处var
关键字的使用是可选的(使用此关键字可以使此文件的本地集合)。
CollectionNameOnServerSide = new Mongo.Collection('employees');
Meteor.publish('employeesPubSub', function() {
return CollectionNameOnServerSide.find({});
});
CollectionNameOnClientSide = new Mongo.Collection('employees');
var employeesData = Meteor.subscribe('employeesPubSub');
Template.templateName.helpers({
'subcribedDataNotAvailable' : function(){
return !employeesData.ready();
},
'employeeNumbers' : () =>{
CollectionNameOnClientSide.find({'empId':1});
}
});
在这里,我们可以使用subcribedDataNotAvailable
帮助方法来了解客户端数据是否准备就绪,如果数据已准备好,则使用employeeNumbers
帮助方法打印员工编号。
<TEMPLATE name="templateName">
{{#if subcribedDataNotAvailable}}
<h1> data loading ... </h1>
{{else}}
{{#each employeeNumbers }}
{{this}}
{{/each}}
{{/if}}
<TEMPLATE>
答案 3 :(得分:0)
// on the server
Meteor.publish('posts', function() {
return Posts.find();
});
// on the client
Meteor.subscribe('posts');