Meteor 3.0 是一个功能强大的全栈 JavaScript 框架,特别适合实时应用程序的开发。它的核心机制之一就包括发布-订阅(Publish-Subscribe)模型,它允许服务器端发布数据,客户端订阅并实时更新。本文将介绍如何在 Meteor 3.0 中使用 publish-subscribe,包括简单发布、自定义发布,以及客户端的订阅和数据读取流程。
前文提要:开发环境的搭建 -全局安装或使用容器镜像 ;容器化开发环境下的meteor工程架构解析 ;运行在浏览器端的NoSQL数据库副本-MiniMongo介绍及其前后端数据实时同步示例;RPC方法注册及调用-更轻量的服务接口提供方式
1. 服务器端:publish
数据源
1.1 简单发布(返回 cursor
或 cursor[]
)
在服务器端,发布数据非常简单。首先,你可以通过 Meteor.publish
来定义一个数据发布函数,该函数通常返回一个 MongoDB 集合的查询游标(cursor)。这个游标代表服务器向客户端发布的数据集。以发布一个简单的 Posts
集合为例:
// posts.js (服务器端代码)
import {
Meteor } from 'meteor/meteor';
import {
Posts } from '/imports/api/posts.js';
Meteor.publish('allPosts', function () {
// 返回一个 cursor 对象,客户端可以订阅该发布源
return Posts.find();
});
上面的代码会将 Posts
集合中的所有数据发布到客户端。客户端订阅后会自动接收到这些数据。
你也可以返回一个数组,其中包含多个游标。例如:
Meteor.publish('multipleCollections', function () {
return [
Posts.find(),
Comments.find(),
];
});
这种方式适合在一次发布中返回来自多个集合的数据。
1.2 自定义发布(使用 added
/ changed
/ removed
/ stop
方法)
有时候你可能需要更灵活的控制,像控制数据发布的具体时机或内容。这时你可以使用 added
、changed
、removed
以及 stop
方法来手动管理数据的发布。
自定义发布的典型例子是只发布部分数据,或者根据某些复杂的逻辑来动态发布。如下示例,发布一个用户拥有的所有 Tasks
集合中的任务:
// tasks.js (服务器端代码)
import {
Meteor } from 'meteor/meteor';
import {
Tasks } from '/imports/api/tasks.js';
Meteor.publish('userTasks', function () {
const self = this;
const userId = self.userId;
if (!userId) {
self.ready();
return;
}
// meteor的minimongo的find返回一个cursor,是可观察的;
// 除了数据集合,最常用的还包括订阅redis或者mq的数据
const handle = Tasks.find({
owner: userId }).observeChanges({
added(id, fields) {
// 这个tasks就是发布到前端哪个数据集合
self.added('tasks', id, fields);
},
changed(id, fields) {
self.changed('tasks', id, fields);
},
removed(id) {
self.removed('tasks', id);
},
});
self.ready();
self.onStop(function () {
handle.stop();
});
});
在此示例中,使用 observeChanges
来监听数据库的变化并调用 added
、changed
和 removed
,从而手动控制数据的发布。
2. 客户端:subscribe
订阅并读取数据
2.1 订阅与传参
在客户端,你可以使用 Meteor.subscribe
来订阅服务器端的发布源。对于简单的发布,订阅不需要任何参数;但对于自定义发布,你可以传递一些参数来过滤数据。例如,订阅上文中的 allPosts
数据源:
// client.js (客户端代码)
Meteor.subscribe('allPosts');
对于带参数的订阅,如根据用户 ID 来订阅特定用户的任务:
Meteor.subscribe('userTasks');
在订阅时还可以传递动态参数,例如只订阅特定状态的任务:
Meteor.subscribe('userTasks', {
status: 'completed' });
2.2 从 MiniMongo 中读取数据并实时更新
在客户端订阅成功后,数据会被同步到客户端的 MiniMongo 数据库中。你可以使用 Mongo.Collection#find()
方法来从 MiniMongo 中检索数据。
// client.js (客户端代码)
import {
Posts } from '/imports/api/posts.js';
Tracker.autorun(() => {
const postsCursor = Posts.find();
postsCursor.forEach(post => {
console.log(post);
});
});
通过 Tracker.autorun
包裹查询,你可以确保每当数据发生变化时,UI 自动重新渲染。例如,你可以将其绑定到一个模板或 React 组件中,实时显示更新的数据。
3. 使用 mapCursor
将 MiniMongo 的 cursor
转化为 Vue 3 响应式数组
在 Meteor 3.0 中,使用 autorun
可以自动跟踪数据的变化。而在 Vue 3 中,响应式系统是通过 reactive
实现的。为了在 Vue 3 的 setup
函数中使用 Meteor 的数据订阅,我们可以定义一个方法 mapCursor
,将 Meteor 的 MiniMongo 游标(cursor)转化为 Vue 3 的响应式数组。
3.1 方法设计
我们将定义一个名为 mapCursor
的函数,它接收一个 Meteor 的 cursor
,例如 Tasks.find({})
,然后利用 Tracker.autorun
来监听游标变化。每当游标中的数据发生变化时,我们将更新 Vue 3 的 reactive
数组。并且,这个方法适合在 Vue 3 的 setup
函数中使用,能够在组件卸载时自动停止 autorun
。
3.2 代码实现
import {
reactive, onUnmounted } from 'vue';
import {
Tracker } from 'meteor/tracker';
export function mapCursor(cursor) {
// 创建一个响应式数组
const reactiveArray = reactive([]);
// 使用 autorun 监控 cursor 的变化
const computation = Tracker.autorun(() => {
// 获取游标中的数据
const data = cursor.fetch();
// 清空 reactiveArray 并用新的数据替换
reactiveArray.length = 0; // 清空数组
data.forEach(item => {
reactiveArray.push(item); // 添加新的数据
});
});
// 使用 onUnmounted 确保组件卸载时停止 autorun
onUnmounted(() => {
computation.stop();
});
// 返回响应式数组
return reactiveArray;
}
3.3 方法说明
reactiveArray
: 使用 Vue 3 的reactive
方法创建一个响应式数组,用来存储cursor
的数据。Tracker.autorun
: 用于监控cursor
的变化。每当 MiniMongo 中的数据发生变化时,它会自动运行,将最新的数据同步到reactiveArray
中。清空并重新填充数组: 当游标中的数据发生变化时,我们先清空
reactiveArray
,然后用新的数据重新填充它。onUnmounted
: 确保当组件卸载时停止autorun
监控,避免内存泄漏。返回值: 返回的是一个 Vue 3 的响应式数组,这个数组会随着 MiniMongo 中数据的变化而自动更新。
3.4 在 Vue 3 中使用 mapCursor
该方法设计为在 Vue 3 的 setup
函数中使用,下面是如何在组件中使用这个方法的示例:
<template>
<div>
<h2>任务列表</h2>
<ul>
<li v-for="task in tasks" :key="task._id">{
{
task.name }}</li>
</ul>
</div>
</template>
<script setup>
import {
Tasks } from '/imports/api/tasks.js';
import {
mapCursor } from '/imports/utils/mapCursor.js';
// 订阅 Tasks 数据
const tasks = mapCursor(Tasks.find({
}));
</script>
总结
Meteor 3.0 的发布-订阅模型使得开发实时应用非常高效。服务器端可以通过简单发布或自定义发布来灵活地控制数据的发布,客户端则可以通过订阅获取数据,并通过 Tracker.autorun
等机制实现自动化的 UI 更新。这种架构能够确保客户端总是与服务器端的数据保持同步,不需要定时或者在某个时机去获取最新数据。