今天我们聊一个在实际项目架构中避不开的问题:跨页面通信(Global Communication)。
当你在"个人中心"修改了头像,如何让已经打开的"首页"也同步更新?Uni-app 提供了几种方案,但场景选错就会导致内存泄露或代码难以维护。
🚀 今日份:Uni-app 全局通信的"三剑客"
1. 简单直接:uni.$emit 与 uni.$on
这是最常用的**事件总线(EventBus)**模式,适合页面层级较深,或者非父子组件间的简单传值。
uni.$emit(eventName, data):发送事件。uni.$on(eventName, callback):监听事件。uni.$off(eventName):移除监听(极其重要)。
⚠️ 避坑指南:
很多开发者发现页面跳了几次后,事件会触发多次。这是因为 uni.$on 是全局监听,页面销毁时它不会自动销毁 。
必须 在 onUnload 中手动移除:
javascript
// A页面监听
onLoad() {
uni.$on('updateUser', (data) => {
this.userInfo = data;
});
},
onUnload() {
// 页面卸载时务必移除,否则会造成内存泄露和逻辑重复执行
uni.$off('updateUser');
}
2. 状态管理:Pinia / Vuex
对于复杂业务逻辑(如购物车数量、登录用户信息、全局主题),推荐使用官方支持的 Pinia(Vue3)或 Vuex(Vue2)。
- 优点:响应式更新,逻辑清晰,适合大型项目。
- 缺点:会有一定的学习成本和代码结构开销。
实战建议:不要把所有数据都塞进 Pinia,只放那些"全应用共享"的数据。
3. 物理存储:uni.setStorage / getStorage
这是持久化存储,把数据存入手机本地缓存。
- 同步版 :
uni.setStorageSync('key', 'value') - 异步版 :
uni.setStorage({key: 'key', data: 'value'})
场景 :存储 Token、用户的搜索历史记录、配置项。
注意:不要频繁读写 Storage 来做页面通信,因为磁盘 I/O 性能远低于内存。
🛠️ 综合对比与场景选择
| 方案 | 持久化 | 响应式 | 适用场景 |
|---|---|---|---|
| 页面传参 | ❌ | ❌ | A 页面跳 B 页面,传递简单 ID。 |
| uni.$emit | ❌ | ❌ | 跨页面通知(如:返回上一页并触发刷新)。 |
| Pinia/Vuex | ❌ (需插件) | ✅ | 全局状态、用户信息、购物车。 |
| Storage | ✅ | ❌ | 登录 Token、本地设置、历史记录。 |
📝 今日实战技巧:优雅的"返回刷新"
假设在"编辑地址"页保存后,要通知"订单确认"页更新地址:
编辑页 (Edit.vue):
javascript
saveAddress() {
// 调用接口成功后
uni.$emit('refreshAddress', { id: 101, name: '新地址' });
uni.navigateBack();
}
订单页 (Order.vue):
javascript
onLoad() {
uni.$on('refreshAddress', (data) => {
this.currentAddress = data;
console.log('地址已动态更新');
});
},
onUnload() {
uni.$off('refreshAddress');
}
💡 核心总结
- 传参 能解决的不要用事件,事件能解决的不要用全局状态。
- 使用
uni.$on记得配套uni.$off,这是初级和中级开发者的分水岭。 - 持久化数据记得考虑空间限制(小程序通常只有 10MB)。