订阅数据
当使用发布时,你需要在客户端创建针对它创建一个订阅。你可以以发布的名称来调用Meteor.subscribe()
方法来实现这些。此时,基于发布开启了订阅,然后服务器开始从底层连接中发送数据,用来确认你的客户端集合包含来自发布的特定数据拷贝。
Meteor.subscribe()
也可以返回一个属性为.ready()
的“订阅句柄”。这是一个响应式的方法,当发布被标记准备就绪时(或者你明确调用this.ready()
时,又或者一个返回游标的最初内容已经发送完毕时)返回true
。
const handle = Meteor.subscribe('lists.public');
中止订阅
订阅句柄还有另一个重要的属性,.stop()
方法。当你在订阅时,确保总是在已经完成订阅后调用.stop()
是非常重要的。这可以使订阅发送的文档从本地Minimongo的缓存中清理掉以及服务端停止为你的订阅所需要的服务继续工作。如果你忘了停止订阅,你将在客户端及服务端消耗不必要的资源。
然而 ,如果你有条件的调用Meteor.subscribe()
,并包含一个响应式的内容(例如autorun
,React中的getMeteorData
)或在一个Blaze组件中通过this.subscribe()
调用,那么Meteor的响应式系统将在适当的时候自动调用this.stop()
。
在UI组件中订阅
把订阅尽可能近的放在数据需要订阅的地方是最好的方式。这会缩小“远距离作用”以及更简单地理解你应用中传递的数据流。如果订阅和取数据被分开了,那么这使如何更改和为什么更改订阅(诸如更改参数)变得不再清晰,将影响游标所指的内容。
这意味着在实践中你需要把你的订阅放在 组件 中调用。在Blaze中,最好的方法是把它放在onCreated()
的回调函数中:
Template.Lists_show_page.onCreated(function() {
this.getListId = () => FlowRouter.getParam('_id');
this.autorun(() => {
this.subscribe('todos.inList', this.getListId());
});
});
在这个代码片段中我们可以看到在Blaze模板中用于订阅的两个重要的技巧:
调用
this.subscribe()
(比使用Meteor.subscribe
更合适)为模板实例附带一个专用的subscriptionsReady()
方法,当所有模板中的订阅已经就绪时,它将返回True。每当响应式方法
this.getListId()
变化时,调用this.autorun
设置一个响应式的内容将重新初始化该订阅。
请在Blaze的文章中阅读更多关于Blaze订阅的内容,以及在UI的文章中了解关于在UI组件中跟踪加载状态。
取数据
订阅的数据将会被放在客户端的集合中。为了在你的UI中使用数据,你需要去查询你客户端的集合。当你去做这些的时候,这里有几个需要遵循的重要规则。
始终使用特定的查询取数据
如果你发布了一个数据的子集,为了在客户端获取这个子集,它可以被模板化,从而对一个集合中的所有有效数据进行简单的查询(例如,Lists.find()
),并且无需重新指定那些你用来在先前发布数据的Mongo选择器。
But if you do this, then you open yourself up to problems if another subscription pushes data into the same collection, since the data returned by Lists.find()
might not be what you expected anymore. In an actively developed application, it's often hard to anticipate what may change in the future and this can be a source of hard to understand bugs.
但在你做这些之前,
Also, when changing between subscriptions, there is a brief period where both subscriptions are loaded (see Publication behavior when changing arguments below), so when doing thing like pagination, it's exceedingly likely that this will be the case.
Fetch the data nearby where you subscribed to it
We do this for the same reason we subscribe in the component in the first place---to avoid action at a distance and to make it easier to understand where data comes from. A common pattern is to fetch the data in a parent template, and then pass it into a "pure" child component, as we'll see it in the UI Article.
Note that there are some exceptions to this second rule. A common one is Meteor.user()
---although this is strictly speaking subscribed to (automatically usually), it's typically over-complicated to pass it through the component hierarchy as an argument to each component. However keep in mind it's best not to use it in too many places as it makes components harder to test.
全局订阅
One place where you might be tempted to not subscribe inside a component is when it accesses data that you know you always need. For instance, a subscription to extra fields on the user object (see the Accounts Article) that you need on every screen of your app.
However, it's generally a good idea to use a layout component (which you wrap all your components in) to subscribe to this subscription anyway. It's better to be consistent about such things, and it makes for a more flexible system if you ever decide you have a screen that doesn't need that data.