meteor-collection-hooks触发条件清单:find/findOne钩子在Meteor 3中的完整方法对照
meteor-collection-hooks触发条件清单:find/findOne钩子在Meteor 3中的完整方法对照
【免费下载链接】meteor-collection-hooksMeteor Collection Hooks项目地址: https://gitcode.com/gh_mirrors/me/meteor-collection-hooks
meteor-collection-hooks 是 Meteor 社区最常用的集合扩展包,为Mongo.Collection提供before/after钩子。但升级到Meteor 3后,find和findOne钩子的触发条件发生了根本性变化:同步方法不再触发、before.find禁止异步、钩子只在异步方法上生效。这份触发条件清单整理了 find/findOne 钩子在 Meteor 3 中的完整方法对照,帮你快速避开"钩子不执行"的坑。
一、先理解 Meteor 3 发生了什么变化
Meteor 3 全面转向异步 API(insertAsync、updateAsync等),但find()出于兼容性仍是同步方法——它必须立刻返回 cursor 实例。这直接导致了两个结果:
before.find钩子必须同步执行,否则find()无法同步返回 cursor;- find 系列钩子只能挂在 cursor 的异步方法(
fetchAsync等)上触发。
这套逻辑封装在 find.js 中:同步的 before 钩子在 cursor 创建时立即执行,after 钩子则在ASYNC_METHODS(countAsync、fetchAsync、forEachAsync、mapAsync)被调用后触发。
二、find 钩子触发条件完整清单
✅ 会触发 find 钩子的方法
| 写法 | before.find | after.find |
|---|---|---|
collection.find({}).fetchAsync() | ✅ 立即触发 | ✅ 触发 |
collection.find({}).countAsync() | ✅ 立即触发 | ✅ 触发 |
collection.find({}).forEachAsync() | ✅ 立即触发 | ✅ 触发 |
collection.find({}).mapAsync() | ✅ 立即触发 | ✅ 触发 |
❌ 不会触发 find 钩子的方法
| 写法 | before.find | after.find |
|---|---|---|
collection.find({}).fetch() | ❌ 不触发 | ❌ 不触发 |
collection.find({}).count() | ❌ 不触发 | ❌ 不触发 |
collection.find({}).forEach() | ❌ 不触发 | ❌ 不触发 |
collection.find({}).map() | ❌ 不触发 | ❌ 不触发 |
注意:before.find只支持同步函数,写成async function会直接抛错Cannot use async function as before.find hook;而after.find同步、异步都支持。
三、findOne 钩子触发条件完整清单
findOne 钩子的规则更简单:只在findOneAsync()上触发,同步的findOne()一律不触发。测试用例 findone.test.js 中明确验证了这一点。
| 写法 | before.findOne | after.findOne |
|---|---|---|
await collection.findOneAsync({}) | ✅ 触发 | ✅ 触发 |
collection.findOne({}) | ❌ 不触发 | ❌ 不触发 |
与 find 不同,before.findOne和after.findOne都支持异步函数,且before.findOne返回false可以中止本次查询(见 findone.js)。它还用Tracker.withComputation保留了 Meteor 3 下的响应式能力,Tracker.autorun内调用findOneAsync依然能自动重跑。
四、find / findOne 钩子完整方法对照表
下面这张速查表总结了两个钩子的全部差异:
| 对比项 | before.find | after.find | before.findOne | after.findOne |
|---|---|---|---|---|
| 支持异步 | ❌ 必须同步 | ✅ 均可 | ✅ 均可 | ✅ 均可 |
| 触发时机 | cursor 创建时 | cursor 异步方法调用后 | findOneAsync调用时 | findOneAsync返回后 |
| 回调参数 | (userId, selector, options) | (userId, selector, options, cursor) | (userId, selector, options) | (userId, selector, options, doc) |
| 返回 false 中止 | ❌ | ❌ | ✅ | ❌ |
| 注册方式 | collection.before.find(fn) | collection.after.find(fn) | collection.before.findOne(fn) | collection.after.findOne(fn) |
所有注册方法都会返回一个钩子控制器,支持.replace(fn)替换和.remove()移除,方便在发布/订阅中动态管理。
五、钩子参数详解
四个钩子共享前三个参数,语义完全一致:
- userId:当前操作用户 ID(客户端可通过模拟
Meteor.userId注入,见 find_userid.test.js); - selector:查询条件对象,可在 before 钩子中直接修改实现软删除等逻辑;
- options:查询选项,如
{ sort, limit, fields }。
唯一区别在第四个参数:after.find拿到的是cursor 实例,after.findOne拿到的是查询结果文档 doc。
六、实用场景:用 before.find 实现软删除过滤
借助 selector 可修改的特性,最经典的用法是软删除全局过滤:
import { Mongo } from 'meteor/mongo' const Posts = new Mongo.Collection('posts') // 全局过滤已删除文档(必须同步函数) Posts.before.find((userId, selector, options) => { selector.deletedAt = { $exists: false } }) Posts.before.findOne((userId, selector, options) => { selector.deletedAt = { $exists: false } }) // 之后所有查询都自动带上软删除条件 const posts = await Posts.find({ author: 'alice' }).fetchAsync()注意:注册在全局集合上的before.find会影响所有查询,包括钩子内部触发的查询,这也是 find_after_hooks.test.js 中专门验证的边界场景。可用options传标记参数来区分业务查询与内部查询。
七、绕过钩子:direct 系列方法
如果某个场景需要跳过钩子,可以用direct前缀:
Posts.direct.find({}).fetchAsync() // 绕过 find 钩子 Posts.direct.findOne({}) // 绕过 findOne 钩子底层实现位于 collection-hooks.js,通过directEnv环境变量标记当前调用,配合directOp/hookedOp实现钩子的启用与旁路。
八、版本兼容性与升级提醒
本包版本v2.1.0,兼容Meteor 2.16+ 到 3.1+。但请注意 v2.0.0 起的破坏性变更:
before.find禁用异步函数(v2 之前允许);- find/findOne 钩子只响应异步方法(
findOneAsync、fetchAsync等); - 同步调用(
findOne()、fetch())不再触发任何钩子。
如果你的旧代码依赖同步方法触发钩子,升级后务必逐条核对 History.md 中的变更记录。官方文档与完整用法见 README.md,类型定义可参考 collection-hooks.d.ts。
结语:三句话记住触发规则
- find 钩子:before 同步执行、after 跟 async 方法走;
- findOne 钩子:只认
findOneAsync; - 同步方法(
findOne/fetch/count):一律不触发。
收藏这份 meteor-collection-hooks 触发条件清单,升级 Meteor 3 时对照排查,钩子失灵的问题就能一次解决。
【免费下载链接】meteor-collection-hooksMeteor Collection Hooks项目地址: https://gitcode.com/gh_mirrors/me/meteor-collection-hooks
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
