uni.$off必须传函数引用而非事件名,因uni-app事件总线基于函数对象引用匹配;若在onload中用箭头函数注册监听器,每次都是新对象,onunload中仅传事件名无法精准移除,导致内存泄漏和重复回调。

uni.$off 必须传函数引用,不能只传事件名
uni-app 的事件总线 uni.$on 和 uni.$off 不是“按名称开关”,而是基于函数对象的引用匹配。如果你在 onLoad 或 onShow 里写 uni.$on('filter-change', () => { ... }),这个箭头函数每次都是新对象,uni.$off('filter-change') 根本找不到它,监听器就永远留在内存里。
正确做法是提前定义具名函数,并在组件 setup 或 data 中保存引用:
export default {
data() {
return {
filterHandler: null
}
},
onLoad() {
this.filterHandler = (payload) => {
this.applyFilter(payload)
}
uni.$on('filter-change', this.filterHandler)
},
onUnload() {
// ✅ 精确移除:传入同一个函数引用
uni.$off('filter-change', this.filterHandler)
}
}
页面卸载时没调用 uni.$off 的典型后果
不清理会导致两个直接问题:
- 旧页面的
filterHandler仍挂在全局事件总线上,一旦其他页面触发uni.$emit('filter-change'),就会执行已销毁组件的方法,报错如Cannot read property 'applyFilter' of null或this is not defined - 多个同名监听器叠加:比如用户反复进入/退出筛选页,每次都会新增一个
filterHandler,最终一次 emit 触发 N 次回调,UI 闪动、请求重复、状态错乱 - 内存泄漏不易察觉:Vue 实例无法被 GC 回收,尤其在 tab 切换或弹窗频繁打开的场景下,页面卡顿会随使用时间缓慢加剧
为什么不能在 onUnload 里只写 uni.$off('filter-change')
uni.$off('filter-change') 是清空所有绑定到该事件名的监听器,不是当前页面专属的。如果其他页面(比如商品列表页、订单页)也监听了 'filter-change',它们的 handler 会被一并干掉,导致功能突然失效。
所以必须成对使用:
-
uni.$on('filter-change', this.handler)—— 注册时传函数引用 -
uni.$off('filter-change', this.handler)—— 卸载时传**同一个**引用 - 避免在
onUnload里调用uni.$off('filter-change')(无参形式)
Vue 3 setup 语法糖下的清理写法
在 <script setup></script> 中,不能直接访问 this,需用 onUnmounted 钩子 + ref 缓存 handler:
import { ref, onUnmounted } from 'vue'
const filterHandler = ref(null)
onMounted(() => {
filterHandler.value = (payload) => {
// 处理过滤逻辑
}
uni.$on('filter-change', filterHandler.value)
})
onUnmounted(() => {
// ✅ 必须传 .value,确保是同一函数对象
uni.$off('filter-change', filterHandler.value)
})
注意:不要用 onBeforeUnmount,因为此时组件 DOM 还未完全卸载,但 uni.$off 要求监听器必须在实例真正释放前解除——onUnmounted 更稳妥。
最容易被忽略的是 handler 的生命周期绑定关系:它必须和组件实例严格同步创建与销毁,中间不能被闭包意外捕获、不能被其他作用域复用,否则引用就断了。











