用途
该技能面向在 Meteor 3 中构建或调试 React 界面的场景。其核心分工是:React 负责渲染和本地 UI 状态,Meteor 负责响应式数据与构建交接;组件消费已授权的数据并调用方法,发布(publications)和方法(methods)仍是服务端权威。
主要能力
- 新建应用:JavaScript 应用运行
meteor create --react <name>,React 也是默认骨架;TypeScript 与 TSX 运行meteor create --typescript <name>。 - 检查项目:查看 package.json、.meteor/packages、.meteor/versions、客户端入口以及 rspack.config.*。骨架和 react-meteor-data 版本可能独立于本技能变化。
- 挂载根节点:在 Meteor.startup 中用 createRoot 挂载一个 React 根。
- 响应式数据选择:为组件选择一种 react-meteor-data 接口——经典 hooks 用于显式加载 UI 和同步 Minimongo 读取;Suspense hooks 仅在上游 Suspense fallback 和 rejection 边界已负责 pending 与 error 状态时使用。
- 具体 API:用 useSubscribe 判断就绪,用 useFind 处理带稳定文档引用的响应式列表,用 useTracker 处理其他 Tracker 值。
- 构建与刷新:Rspack 从应用的 npm 依赖检测 React,启用 JSX 或 TSX 的 SWC 解析,并为客户端开发安装和注入 React Refresh;普通 React Rspack 应用无需手动添加 refresh 插件。
- 测试:在浏览器支持的客户端套件中测试 UI 行为,并始终卸载已渲染的根,以便 Tracker 计算和订阅能够停止。
使用方式
当前脚手架命令:
meteor create --react my-app
cd my-app
meteor
当前 Meteor 3.4+ 脚手架使用 Rspack 和 react-meteor-data。检查 .meteor/versions:Meteor 3 要求 3.0.0+,这些示例面向 4.0.1,references/react-meteor-data.md 记录了功能下限。
入口示例:
import { Meteor } from "meteor/meteor";
import { createRoot } from "react-dom/client";
import { App } from "/imports/ui/App";
import "/imports/ui/styles.css";
Meteor.startup(() => {
const container = document.getElementById("react-target");
if (!container) throw new Error("Missing #react-target");
createRoot(container).render(<App />);
});
TypeScript 使用 client/main.tsx、tsconfig.json,通常还有 rspack.config.ts。Meteor 会直接发现该 TypeScript 配置。
响应式数据选择对照:
- 订阅就绪:经典 useSubscribe,返回 isLoading 函数,需调用它。
- 响应式列表:经典 useFind,返回 cursor,绝不 fetch()。
- 用户、计数、单文档、ReactiveVar:经典 useTracker,返回响应式值,避免任意副作用。
- 带 Suspense 的异步值:Suspense useTracker,提供稳定 key 和产生 Promise 的函数。
- 支持 SSR 的集合查询:Suspense useFind,传入集合和 find 参数元组。
- 已有类组件:withTracker,在聚焦维护期间保留,另行规划 hooks。
经典列表脚手架示例:
import { useFind, useSubscribe } from "meteor/react-meteor-data";
import { Tasks } from "/imports/api/tasks";
export function TaskList({ listId }) {
const isLoading = useSubscribe("tasks.byList", listId);
const tasks = useFind(
() => Tasks.find({ listId }, { sort: { createdAt: -1 } }),
[listId],
);
if (isLoading()) return <p>Loading...</p>;
if (tasks.length === 0) return <p>No tasks.</p>;
return <ul>{tasks.map((task) => <li key={task._id}>{task.title}</li>)}</ul>;
}
useFind 负责 cursor 观察,仅更新发生变化的文档引用。当转换后的聚合更清晰时使用 useTracker。在调整依赖或 skipUpdate 前阅读 references/react-meteor-data.md。
Suspense 与异步响应式:Suspense 导入会改变签名,不是直接替换。示例:
import { Suspense } from "react";
import { useFind, useSubscribe } from "meteor/react-meteor-data/suspense";
import { Tasks } from "/imports/api/tasks";
function TaskList({ listId }) {
useSubscribe("tasks.byList", listId);
const tasks = useFind(Tasks, [
{ listId },
{ sort: { createdAt: -1 } },
], [listId]);
return <ul>{tasks.map((task) => <li key={task._id}>{task.title}</li>)}</ul>;
}
export function TaskScreen({ listId }) {
return (
<AppErrorBoundary>
<Suspense fallback={<p>Loading...</p>}>
<TaskList listId={listId} />
</Suspense>
</AppErrorBoundary>
);
}
将 Suspense useSubscribe 视为仅用于挂起的调用。在当前审计的包中,其声明的返回类型与运行时值不一致,因此不要消费其返回值。关于稳定 key、rejection、SSR 以及 await 后的 Tracker.withComputation,阅读 references/suspense-and-async.md。
构建与刷新归属:保持 rspack.config.* 最小化。生成的 JavaScript 骨架演示可选的 SVGR 规则;TypeScript 骨架演示类型检查。通过 @meteorjs/rspack 的 defineConfig 组合自定义规则,不要替换 Meteor 的 SWC 默认值。对于 React Compiler,Meteor 3.6-beta.0 配对支持 SWC;更早的配对可保留 Babel。阅读编译器设置以了解 React 版本目标和运行时要求。
职责划分:React 检测、JSX 或 TSX、React Refresh 边界属于本技能;通用 rspack.config 辅助、SWC、别名、缓存、chunks 属于 meteor-modern-build-stack;将现有应用或旧构建插件转换为 Rspack 属于 migrate-to-rspack;Meteor-bundler HMR 或自定义 module.hot 生命周期先看构建技能,再看本技能处理 React 症状。
如果编辑导致页面重载或组件状态重置,先确定是哪个 bundler 编译了该模块。在 Rspack 客户端图中,检查 React Refresh 边界并避免第二个 react-fast-refresh 栈。在 Meteor-bundler 图中,验证 hot-module-replacement 和捆绑的 React Fast Refresh 集成。参见 references/build-refresh-and-testing.md。
变更与测试:从事件处理器调用 Meteor.callAsync,在 React 状态中建模 pending 和 rejection,将验证和授权留在方法内部。不要从组件直接写入敏感集合。测试时,将接受数据作为 props 的展示组件与拥有 hook 的容器分离。用项目现有的 React 测试库对前者做单元测试。在浏览器客户端中用受控 Minimongo 或真实发布测试后者。断言渲染行为,而不是响应式函数调用次数,因为 React 可能丢弃工作并多次调用初始渲染逻辑。
适用场景
- 在 Meteor 3 中新建 React 应用或 TypeScript/TSX 应用。
- 为组件选择经典 hooks 或 Suspense hooks 处理响应式数据。
- 调试 Rspack 下的 React 检测、JSX/TSX 解析与 Fast Refresh 行为。
- 编写涉及订阅、Minimongo 读取、方法调用与 UI 行为的测试。
限制与反模式
- 将经典 isLoading 当作布尔值。它是函数,必须调用。
- 把 Tasks.find().fetch() 传给 useFind。应返回 cursor。
- 在 useFind 工厂内放置订阅。应单独订阅。
- 在 useTracker 响应式函数内调用 React 状态设置器、方法、分析或任意副作用。
- 强制所有经典组件使用 Suspense。两种接口都仍然有用。
- 为不相关的已挂载计算复用通用 Suspense key。
- 在 await 之后仅读取 Tracker 源而不恢复计算。
- 当 Meteor 已注入时,仍向项目配置添加 @rspack/plugin-react-refresh。
- 为添加一个 React 转换而替换所有 Rspack SWC 选项。应扩展它们。
- 在组件集成测试中 mock 掉所有 Meteor 数据行为。
权威资源包括 Meteor React 教程、react-meteor-data 文档、meteor/react-packages 源码与变更日志、Meteor Rspack 集成、React createRoot、React Suspense 以及 references/eval-cases.md。