uni.$emit 和 uni.$on 必须成对出现且监听需在发布前注册,页面卸载时须用 uni.$off 精准清理非匿名回调,推荐统一命名空间如 'app:user-login' 避免冲突。
直接用 uni.$emit 发事件,但不配 uni.$on 监听就收不到;光监听不清理,页面跳走后还会被反复触发——这是最常踩的两个坑。
uni.$emit 和 uni.$on 必须成对出现,且顺序不能反
发布(uni.$emit)永远发生在监听(uni.$on)之后。如果先 uni.$emit('data-ready', {x: 1}),再在另一个页面 uni.$on('data-ready', ...),这次事件就丢了,回调永远不会执行。
- 典型场景:登录页调用
uni.$emit('login-success', userInfo),首页、订单页、个人中心页都需提前在onLoad里注册uni.$on('login-success', ...) - 不能把
uni.$on写在methods或按钮点击里——那等于“每次点一下才开始订阅”,错过之前所有emit - 事件名必须完全一致,区分大小写,推荐全小写 + 中划线,比如
'user-profile-updated',避免拼错
页面卸载时必须用 uni.$off 清理监听器
不清理会导致旧页面的回调函数一直挂在内存里,不仅浪费资源,还可能在新页面触发时执行已销毁组件的逻辑,报 Cannot read property 'xxx' of undefined 这类错误。
- 清理方式要精确:用
uni.$off('login-success', this.loginHandler),而不是只传事件名——否则会清掉所有同名监听,影响其他页面 - 监听函数不能是匿名函数:
uni.$on('x', () => {...})无法被uni.$off移除,必须提前定义并保存引用 - 生命周期钩子选
onUnload(页面关闭/跳转时),不是onHide(只是隐藏,还活着)
uni.$once 更适合“一次性通知”场景
比如从 A 页面跳到 B 页面,只希望 B 页面在首次加载时接收一次参数,后续刷新或重复进入都不再响应——这时 uni.$once 比 uni.$on + 手动 uni.$off 更安全简洁。
- 常见误用:用
uni.$once做登录状态同步——用户登出再登录,第二次uni.$emit就没人听了 -
uni.$once不需要手动uni.$off,它自己触发完就自动解绑 - 仍需注意:监听必须在
uni.$emit前注册,否则照样收不到
真正容易被忽略的是事件名的管理:多个团队成员各自起名,很快就会出现 'update'、'updateData'、'data-update' 三套监听,互相干扰。建议统一前缀,比如 'app:user-login'、'order:status-change',靠命名空间降低冲突风险。











