在 uni-app 开发中,跨页面、跨组件的事件通信是一个常见需求。除了通过 Vuex 或全局状态管理工具外,uni-app 提供了简洁的事件机制,即 uni.$onuni.$offuni.$onceuni.$emit。它们可以理解为一个简化版的全局事件总线,方便页面之间快速通信。

本文将详细介绍这四个方法的作用、用法及注意事项,并结合实际案例帮助大家掌握。


一、基础概念

  • uni.$on(eventName, callback)
    注册一个全局事件监听器,用于监听某个事件。
  • uni.$emit(eventName, data)
    触发一个全局事件,并携带数据,通知监听者执行回调。
  • uni.$off(eventName, callback?)
    移除事件监听器,可以移除指定事件,也可以移除所有事件。
  • uni.$once(eventName, callback)
    注册一个只执行一次的事件监听器,执行后自动移除。

二、常用场景

  1. 页面 A 更新数据,通知页面 B 刷新列表
  2. 弹窗关闭时,通知父组件更新状态
  3. 登录成功后,通知多个页面同步用户信息
  4. 消息推送触发事件,应用内多个页面响应

三、使用示例

1. uni.$onuni.$emit 配合

页面 A(触发方)
<template><view><button @click="sendMessage">发送消息</button></view>
</template><script>
export default {methods: {sendMessage() {uni.$emit('refreshList', { msg: '页面A发来刷新指令', time: Date.now() });}}
}
</script>
页面 B(监听方)
<template><view><text>接收到的消息:{{ message }}</text></view>
</template><script>
export default {data() {return {message: '暂无消息'}},onLoad() {// 注册事件监听uni.$on('refreshList', (data) => {console.log('收到事件:', data);this.message = data.msg + ' at ' + data.time;});},onUnload() {// 页面卸载时移除监听,防止内存泄漏uni.$off('refreshList');}
}
</script>

2. uni.$once 示例

uni.$once 会在第一次触发后自动注销,适合只执行一次的场景,比如 登录成功通知

<script>
export default {onLoad() {uni.$once('loginSuccess', (user) => {console.log('用户登录成功:', user);this.username = user.name;});}
}
</script>

在登录页面:

uni.$emit('loginSuccess', { name: '张三', id: 1001 });

3. uni.$off 用法

移除单个事件
uni.$off('refreshList');
移除多个事件
uni.$off(['refreshList', 'loginSuccess']);
移除所有事件
uni.$off();

⚠️ 注意:如果不在页面卸载时移除事件监听,可能会导致事件被多次触发或内存泄漏。


四、最佳实践与注意事项

  1. 事件注册和注销成对出现
    建议在 onLoadcreated 中注册,在 onUnloadbeforeDestroy 中注销。
  2. 避免过度使用全局事件
    小型通信可用,复杂业务建议使用 Vuex 或 Pinia 来做状态管理。
  3. 防止重复注册
    如果在一个页面多次进入并注册事件,而没有注销,可能会导致回调执行多次。
  4. 调试建议
    可以在事件触发和监听时 console.log 日志,方便追踪。

五、总结

  • uni.$on:注册全局事件监听
  • uni.$emit:触发事件并传递数据
  • uni.$once:只执行一次的事件监听
  • uni.$off:移除监听,防止内存泄漏

它们本质上是 uni-app 提供的全局事件总线,简单易用,适合中小型项目的跨页面通信。

在实际开发中,如果项目规模较大,推荐用 Vuex/Pinia 做数据驱动;如果只是临时通信,使用 uni.$on 等方法就足够了。