Appearance
发布-订阅模式
¥Pub-sub
Who maintains the package
- Jam
这是个什么包?
¥What is this package?
jam:pub-sub
为 Meteor 应用带来了三个关键特性:
¥jam:pub-sub
brings three key features to Meteor apps:
基于方法的发布/订阅
¥Method-based publish / subscribe
更改基于流的发布/订阅
¥Change Streams-based publish / subscribe
订阅缓存
¥Subscription caching
重要提示:此包要求你使用
v2.8.1
中引入的基于 Promise 的*Async
Meteor 集合方法。¥Important: This package expects that you'll use the promise-based
*Async
Meteor collection methods introduced inv2.8.1
.
基于方法的发布/订阅
¥Method-based publish / subscribe
Meteor 的传统 publish / subscribe
确实非常棒。但是,被动地将更新推送到所有连接的客户端是有成本的 - 它会占用大量资源,最终会限制你扩展应用的能力。
¥Meteor's traditional publish / subscribe
is truly wonderful. However, there is a cost to pushing updates reactively to all connected clients – it's resource intensive and will eventually place limits on your ability to scale your app.
减少对传统 publish / subscribe
需求的一种方法是通过 Meteor Method
获取数据,但这里存在一个大问题:数据不会自动合并到 Minimongo 中,你将完全失去 Meteor 的神奇反应能力。Minimongo 非常易于使用,作为客户端的数据源,它让一切变得简单。如果没有它,你需要在客户端创建自己的存储,本质上是复制 Minimongo。
¥One way to reduce the need for the traditional publish / subscribe
is to fetch the data via a Meteor Method
but there's a big problem here: the data won't be automatically merged into Minimongo and you completely lose Meteor's magical reactivity. Minimongo is great to work with and makes things easy as the source of truth on the client. Without it, you'll need to create your own stores on the client and essentially duplicate Minimongo.
使用 jam:pub-sub
,你可以使用 Meteor.publish.once
和相同的 Meteor.subscribe
通过 Meteor 方法获取数据,并在 Minimongo 中自动合并,以便你可以按照习惯的方式使用它。它还会在用户进行数据库写入时自动保留响应式。请注意,这些写入操作在设计上不会实时广播到所有连接的客户端,但在许多情况下,你可能会发现不需要 Meteor 传统 publish / subscribe
的该功能。
¥With jam:pub-sub
, you use Meteor.publish.once
and the same Meteor.subscribe
to have the data fetched via a Meteor Method and merged automatically in Minimongo so you can work with it as you're accustomed to. It also automatically preserves reactivity for the user when they make database writes. Note that these writes will not be broadcast in realtime to all connected clients by design but in many cases you might find that you don't need that feature of Meteor's traditional publish / subscribe
.
如何下载?
¥How to download it?
将软件包添加到你的应用
¥Add the package to your app
bash
meteor add jam:pub-sub
来源
¥Sources
如何使用?
¥How to use it?
更改基于流的发布/订阅
¥Change Streams-based publish / subscribe
Alpha
使用 jam:pub-sub
和 MongoDB 变更流,你可以保留 Meteor 对所有客户端的神奇响应能力,同时选择不使用传统的 publish / subscribe
及其对 oplog
的使用。使用 Meteor.publish.stream
而不是 Meteor.publish
,并在客户端上使用相同的 Meteor.subscribe
进行订阅。
¥With jam:pub-sub
and MongoDB Change Streams, you can preserve Meteor's magical reactivity for all clients while opting out of the traditional publish / subscribe
and its use of the oplog
. Use Meteor.publish.stream
instead of using Meteor.publish
and subscribe using the same Meteor.subscribe
on the client.
重要提示:当你使用的过滤器可以共享时,变更流将发挥最佳作用。为此,如果你的发布物包含 userId
,此软件包将在设置变更流时过滤掉该条件,因为它会导致过多的唯一变更流。例如,假设你有以下发布:
¥Important: Change Streams will work best when the filter you use can be shared. To that end, if you have a publication that includes a userId
, this package will filter out that condition when setting up the Change Stream because it will result in too many unique change streams. As an example, lets say you have this publication:
js
Meteor.publish.stream('todos', function() {
return Todos.find({
$or: [
{ isPrivate: false },
{ owner: this.userId }
]
});
});
调用此发布时,它将提取所有与上述过滤器匹配的 Todos
,然后开始使用此过滤器监视变更流:
¥When this publication is invoked, it will pull all the Todos
that match the filter above and then begin watching a Change Stream with this filter:
js
{ isPrivate: false }
对于这个特定的过滤器,它的行为应该符合你的预期,因此你无需进行任何更改。但是,如果你有涉及 userId
的复杂过滤器,则需要确保在使用 .stream
时也能保持你期望的行为。如果这不能满足你的需求,你可以将一个发布拆分成两个发布,使用带有可共享过滤器的 .stream
和用于 userId
的 .once
:
¥For this particular filter, it should behave as you'd expect so you wouldn't need to make changes. However, if you have complex filters involving the userId
, you'll need to be sure that the behavior you expect remains when using .stream
. If it's not meeting your needs, you could split the one publication into two publications, using a .stream
with a filter than can be shared and a .once
for the userId
:
js
Meteor.publish.stream('todos.public', function() {
return Todos.find({ isPrivate: false });
});
Meteor.publish.once('todos.owned', function() {
return Todos.find({ owner: this.userId });
});
拆分为两个的缺点是可能会导致过度获取,但数据将正确合并到 Minimongo 中。
¥The downside by splitting into two is it could result in over-fetching but the data will be merged correctly into Minimongo.
注意:在大多数情况下,尽可能使用 Meteor.publish.once
,仅在真正需要时使用 Meteor.publish.stream
,并且使用可共享的过滤器,这样可能会最有利。
¥Note: In most cases, you'd likely benefit the most from using Meteor.publish.once
anywhere you can and using Meteor.publish.stream
only when you really need it and with a filter than can be shared.
注意:如果你决定完全不使用传统的 Meteor.publish
,那么你还需要完全禁用 oplog
- 将 disable-oplog
包与 meteor add disable-oplog
包一起添加。
¥Note: If you decide to entirely opt-out of using the traditional Meteor.publish
, then you'll also want to disable the oplog
entirely — add the disable-oplog
package with meteor add disable-oplog
.
目前,此功能被认为处于 alpha
状态。根据 Meteor 社区之前的 变更流实验,似乎使用变更流作为传统 publish / subscribe
的全面替代品可以 "只是工作"。但是,实际上,根据写入频率、连接的客户端数量、数据建模方式以及在 Meteor.publish.stream
内部设置游标的方式,它可能属于 "你的实际效果可能有所不同" 类型的情况。话虽如此,如果你对此功能感兴趣,我鼓励你尝试一下并分享你的发现。
¥At the moment, this feature is considered in an alpha
state. Based on previous Change Streams experiments by the Meteor Community, it seems that using Change Streams as a wholesale replacement for the traditional publish / subscribe
could "just work". However, in practice it may be a "Your Mileage May Vary" type of situation depending on the frequency of writes, number of connected clients, how you model your data, and how you set up the cursors inside of Meteor.publish.stream
. With that said, if you're interested in this feature, I'd encourage you to try it out and share your findings.
订阅缓存
¥Subscription caching
通常,当用户在路由或组件之间移动时,订阅将被停止。当用户在你的应用中来回导航时,每次都会导致重新订阅,这意味着更多的旋转图标、更慢的体验,并且通常是一种浪费。
¥Normally, when a user moves between routes or components, the subscriptions will be stopped. When a user is navigating back and forth in your app, each time will result in a re-subscribe which means more spinners, a slower experience, and is generally a waste.
通过缓存订阅,你可以为用户创造更好的用户体验。由于订阅本身已被缓存,Minimongo 中的数据将在后台更新,直到该订阅的 cacheDuration
到期,届时 Minimongo 将停止运行,数据也将按预期从 Minimongo 中删除。
¥By caching your subscriptions, you can create a better user experience for your users. Since the subscription itself is being cached, the data in Minimongo will be updated in the background until the cacheDuration
expires for that subscription at which point it will be stopped and the data will be removed from Minimongo as expected.
用法
¥Usage
将软件包添加到你的应用
¥Add the package to your app
meteor add jam:pub-sub
定义基于方法的发布
¥Define a Method-based publication
使用 Meteor.publish.once
定义发布并像当前一样订阅。Meteor.publish.once
期望你像 Meteor.publish
一样返回一个游标或游标数组。
¥Define a publication using Meteor.publish.once
and subscribe just as you do currently. Meteor.publish.once
expects you to return a cursor or an array of cursors just like Meteor.publish
.
js
// server
Meteor.publish.once('notes.all', function() {
return Notes.find();
});
js
// client
// Since each view layer (Blaze, React, Svelte, Vue, etc) has a different way of using `Tracker.autorun`, I've omitted it for brevity. You'd subscribe just as you do currently in your view layer of choice.
Meteor.subscribe('notes.all')
// work with the Notes collection in Minimongo as you're accustomed to
Notes.find().fetch();
就是这样。使用 Meteor.publish.once
后,它将首先获取数据并自动将其合并到 Minimongo 中。对 Notes
集合的任何数据库写入都将以响应式方式发送给执行写入的用户。
¥That's it. By using Meteor.publish.once
, it will fetch the data initally and automatically merge it into Minimongo. Any database writes to the Notes
collection will be sent reactively to the user that made the write.
重要提示:命名发布物时,请务必包含集合名称。这通常是常见的做法,此软件包也依赖于该约定。如果你不执行此操作并且正在缓存订阅,则订阅停止时,Minimongo 数据可能会被意外删除或保留。建议你在包括
Meteor.publish
在内的所有发布中都遵循此约定。以下是在发布名称中包含集合名称的一些示例:¥Important: when naming your publications be sure to include the collection name(s) in it. This is generally common practice and this package relies on that convention. If you don't do this and you're caching the subscription, Minimongo data may be unexpectedly removed or retained when the subscription stops. It's recommended that you follow this convention for all publications including
Meteor.publish
. Here are some examples of including the collection name in the publication name:
js
// the name you assign inside Mongo.Collection should be in your publication name(s), in this example 'notes'
const Notes = new Mongo.Collection('notes')
// as long as it appears somewhere in your publication name, you're good to go. here are some examples:
Meteor.publish.once('notes');
Meteor.publish.once('notes.all');
Meteor.publish.once('notes/single');
Meteor.publish.once('somethingAndNotes');
它的工作方式与游标数组相同:
¥It also works just as you'd expect for an array of cursors:
js
// server
Meteor.publish.once('notes.todos.all', function() {
return [Notes.find(), Todos.find()];
});
js
// client
Meteor.subscribe('notes.todos.all');
// work with the Notes collection in Minimongo as you're accustomed to
Notes.find().fetch();
// work with the Todos collection in Minimongo as you're accusomted to
Todos.find().fetch();
在 Meteor.publish.once
中,this.userId
和 this.added 仍然可以使用。添加的文档将包含在最终结果数据中。其余底层 publish
API 将被忽略,因为它们不再适用于基于方法的数据获取。
¥Inside Meteor.publish.once
, this.userId
and this.added can still be used. The added document will be included in the final result data. The rest of the low-level publish
API will be disregarded, as they no longer fit into the context of a Method-based data fetch.
js
Meteor.publish.once('notes.all', function() {
// ... //
const userId = this.userId;
this.added('notes', _id, fields);
// ... //
return Notes.find();
})
定义基于变更流的发布
¥Define a Change Streams-based publication
使用 Meteor.publish.stream
定义发布并像当前一样订阅。Meteor.publish.stream
期望你像 Meteor.publish
一样返回一个游标或游标数组。
¥Define a publication using Meteor.publish.stream
and subscribe just as you do currently. Meteor.publish.stream
expects you to return a cursor or an array of cursors just like Meteor.publish
.
js
// server
Meteor.publish.stream('notes.all', function() {
return Notes.find();
});
js
// client
// Since each view layer (Blaze, React, Svelte, Vue, etc) has a different way of using `Tracker.autorun`, I've omitted it for brevity. You'd subscribe just as you do currently in your view layer of choice.
Meteor.subscribe('notes.all')
// work with the Notes collection in Minimongo as you're accustomed to
Notes.find().fetch();
就是这样。使用 Meteor.publish.stream
后,任何对 Notes
集合的数据库写入都将像 Meteor.publish
一样被动地发送给所有连接的客户端。
¥That's it. By using Meteor.publish.stream
, any database writes to the Notes
collection will be sent reactively to all connected clients just as with Meteor.publish
.
设置变更流的 maxPoolSize
¥Setting the maxPoolSize
for Change Streams
maxPoolSize
默认为 100
,可能不需要调整。如果你需要调整,可以在 Meteor.settings 中像这样设置:
¥maxPoolSize
defaults to 100
which may not need adjusting. If you need to adjust it, you can set it in Meteor.settings like this:
js
{
//...//
"packages": {
"mongo": {
"options": {
"maxPoolSize": 200 // or whatever is appropriate for your application
}
}
}
// ... //
}
返回关于订阅缓存
¥Turn on subscription caching
使用 jam:pub-sub
,你可以全局或在每个订阅级别启用订阅缓存。订阅缓存默认关闭,以保留 Meteor 的当前行为。任何订阅都可以被缓存,无论其发布方式如何。
¥With jam:pub-sub
, you can enable subscription caching globally or at a per-subscription level. Subscription caching is turned off by default to preserve the current behavior in Meteor. Any subscription can be cached, regardless of how it's published.
要为每个订阅全局启用订阅缓存:
¥To enable subscription caching globally for every subscription:
js
// put this in a file that's imported on the client at a minimum. it can be used isomorphically but the configuration only applies to the client.
import { PubSub } from 'meteor/jam:pub-sub';
PubSub.configure({
cache: true // defaults to false
});
全局 cacheDuration
默认设置为 60 seconds
。这是从最初将订阅设置为停止时开始的,即,当包含订阅的组件因用户离开而被销毁时。如果用户立即返回,则将使用缓存。如果没有,则在 60 seconds
之后,订阅缓存将被删除。如果你要更改全局 cacheDuration
,请使用 seconds
中的值进行更改:
¥The global cacheDuration
is set to 60 seconds
by default. This is from when the subscription was originally set to be stopped, i.e. when the component housing the subscription was destroyed because the user navigated away. If the user comes right back, then the cache will be used. If they don't, after 60 seconds
, the subscription cache will be removed. If you want to change the global cacheDuration
, change it with a value in seconds
:
js
import { PubSub } from 'meteor/jam:pub-sub';
PubSub.configure({
cacheDuration: 5 * 60 // sets the cacheDuration to 5 minutes. defaults to 1 min
});
使用 Meteor.subscribe
时,你还可以为每个单独的订阅配置 cache
和 cacheDuration
。例如:
¥You can also configure cache
and cacheDuration
for each individual subscription when you use Meteor.subscribe
. For example:
js
Meteor.subscribe('todos.single', _id, { cacheDuration: 30 }) // caches for 30 seconds, overriding the global default
Meteor.subscribe('notes.all', { cache: true }) // turns caching on, overriding the global default, and uses the global default cacheDuration
注意:Meteor.subscribe API 的其余部分(例如
onStop
、onReady
)将按预期工作。¥Note: the rest of the Meteor.subscribe API (e.g.
onStop
,onReady
) works just as you'd expect.
注意:由于订阅缓存期间数据将保留在 Minimongo 中,因此你应该注意 Minimongo
.find
选择器。请务必使用特定的选择器来.find
特定订阅所需的数据。这通常被认为是 最佳实践,因此这主要是一个有用的提醒。¥Note: Because the data will remain in Minimongo while the subscription is cached, you should be mindful of your Minimongo
.find
selectors. Be sure to use specific selectors to.find
the data you need for that particular subscription. This is generally considered best practice so this is mainly a helpful reminder.
清除缓存
¥Clearing the cache
每个单独的订阅在其 cacheDuration
错误过期后将自动从缓存中删除。
¥Each individual subcription will be automatically removed from the cache when its cacheDuration
elapses.
虽然这不是必需的,但你可以通过编程方式清除所有缓存的订阅:
¥Though it shouldn't be necessary, you can programmatically clear all cached subscriptions:
js
import { PubSub } from 'meteor/jam:pub-sub';
PubSub.clearCache();