做 uni-app 小程序开发的同学,尤其是需要兼容鸿蒙环境的,大概率都踩过 URL 拼接 & 传递多参数 的坑 —— 要么参数丢失、要么特殊字符解析异常、要么鸿蒙环境直接不兼容。
今天给大家分享一种更优雅、更稳定的多参数传递方案:eventChannel 事件通道,亲测在微信小程序、支付宝小程序、鸿蒙环境下都能稳定运行,告别 URL 拼接的各种糟心事。
一、为什么不推荐 URL 拼接 & 传递多参数?
先说说我们以前常用的 URL 拼接方式,代码大概是这样的:
// 不推荐的 URL 拼接方式
uni.navigateTo({
url: `/pages/work/partslist/expanded-list?id=${item.id}&fileSrc=${item.fileSrc}`
});
这种方式在简单场景下可行,但存在明显弊端:
- 兼容性差:鸿蒙环境对 uni-app 的 URL 拼接支持不友好,容易出现参数丢失、页面跳转失败的问题。
- 特殊字符问题:如果
fileSrc是一个完整的图片 / 文件链接(包含http://、/、?等特殊字符),会被解析为 URL 的一部分,导致接收页获取的参数错乱。 - 可读性差:多参数拼接后,URL 冗长杂乱,后期维护困难。
- 数据类型限制:只能传递字符串类型,数字、对象等类型需要手动转换,容易出现类型错误。
而 eventChannel 作为 uni-app 提供的官方事件通道方案,完美解决了以上所有问题 —— 支持传递任意类型数据、无需处理特殊字符、兼容性更好、代码更优雅。
二、完整实现方案(发送页 + 接收页)
前置说明
- 适用场景:
uni.navigateTo、uni.redirectTo页面跳转(不支持uni.switchTab,因为switchTab会关闭其他页面)。 - 核心逻辑:发送页跳转成功后,通过
eventChannel发送数据;接收页在生命周期中获取eventChannel,监听并接收数据。
第一步:发送页(跳转页)实现
这是触发页面跳转的代码,重点在 uni.navigateTo 的 success 回调中发送数据:
// 列表点击或其他触发跳转的方法
handleJumpToExpandedList(item) {
// 先做数据校验,避免空数据传递
if (!item || (!item.id && !item.fileSrc)) {
uni.showToast({
title: '数据异常,无法跳转',
icon: 'none'
});
return;
}
uni.navigateTo({
// 注意:URL 无需拼接任何参数,保持纯净路径即可
url: `/pages/work/partslist/expanded-list`,
success: (res) => {
// 跳转成功后,获取事件通道并发送数据
res.eventChannel.emit('passData', {
id: item.id, // 支持数字、字符串等类型
fileSrc: item.fileSrc, // 支持完整链接、特殊字符,无需转义
// 还可以传递对象、数组等复杂数据,示例:
// info: { name: '测试', status: 1 },
// list: [1, 2, 3]
});
},
fail: (err) => {
console.error('页面跳转失败:', err);
uni.showToast({
title: '页面跳转失败',
icon: 'none'
});
}
});
}
发送页关键要点
- URL 保持纯净,不拼接任何参数,避免解析冲突。
res.eventChannel.emit('事件名', 传递的数据):第一个参数是自定义事件名(后续接收页需要对应),第二个参数是任意格式的数据源(对象、数组、基本类型均可)。- 增加数据校验和
fail回调,提升代码健壮性,避免用户看到空白页面或报错。
第二步:接收页(目标页)实现
目标页需要获取事件通道,并监听发送页定义的事件,从而接收数据,核心注意点:调用时机!
<template>
<!-- 你的页面结构 -->
<view>接收的商品ID:{{ goodsId }}</view>
<view>接收的文件链接:{{ fileSrc }}</view>
</template>
<script>
export default {
data() {
return {
goodsId: '', // 接收的id
fileSrc: '', // 接收的文件链接
// info: {}, // 接收复杂对象(可选)
// list: [] // 接收数组(可选)
};
},
onLoad() {
// 关键:必须在 onLoad / onShow 生命周期中获取事件通道
// 鸿蒙环境下,created 生命周期中调用会返回 undefined,踩坑!
const eventChannel = this.getOpenerEventChannel();
if (eventChannel) {
// 监听发送页定义的 'passData' 事件,与发送页的事件名保持一致
eventChannel.on('passData', (data) => {
console.log('接收成功的数据:', data);
// 赋值给当前页面的变量,用于后续业务逻辑
if (data.id) {
this.goodsId = data.id;
}
if (data.fileSrc) {
this.fileSrc = data.fileSrc;
}
// 复杂数据赋值(可选)
// if (data.info) {
// this.info = data.info;
// }
// 调用依赖参数的初始化方法(如请求接口、渲染列表)
this.initListData();
});
} else {
console.error('获取事件通道失败,无法接收参数');
}
},
methods: {
// 你的业务初始化方法(依赖接收的参数)
initListData() {
if (!this.goodsId) {
console.warn('商品ID为空,无法正常初始化列表');
return;
}
// 后续业务逻辑:如根据 goodsId 和 fileSrc 请求接口
console.log('初始化列表,参数:', this.goodsId, this.fileSrc);
// ...你的接口请求、列表渲染等代码
}
}
};
</script>
接收页关键要点(避坑重点!)
- 调用时机:
this.getOpenerEventChannel()必须在onLoad或onShow生命周期中调用,不能在created或非生命周期中调用,尤其是鸿蒙环境下,否则会返回undefined,无法获取事件通道。 - 事件名一致:
eventChannel.on('passData', ...)中的事件名,必须和发送页emit的事件名完全一致(大小写敏感)。 - 数据判空:接收数据后做判空处理,避免因数据异常导致后续业务逻辑报错。
三、鸿蒙环境额外注意事项
- 避免在接收页延迟调用
getOpenerEventChannel(),页面加载完成后再调用,大概率会获取失败。 - 传递复杂数据(如大对象、长数组)时,优先使用
eventChannel,比storage更高效,且不会造成缓存污染。 - 数据类型保持一致:发送页传递的数字类型,接收页无需手动转换,直接使用即可,避免类型不匹配导致的业务问题。
四、备选方案(若 eventChannel 偶发兼容问题)
如果在个别特殊环境下,eventChannel 出现偶发问题,可以使用 uni.setStorageSync 临时存储参数作为备选,步骤如下:
// 发送页:临时存储参数
handleJumpToExpandedList(item) {
uni.setStorageSync('expandedListParams', {
id: item.id,
fileSrc: item.fileSrc
});
uni.navigateTo({
url: `/pages/work/partslist/expanded-list`
});
}
// 接收页 onLoad 中读取并删除临时存储
onLoad() {
const params = uni.getStorageSync('expandedListParams');
if (params) {
this.goodsId = params.id || '';
this.fileSrc = params.fileSrc || '';
this.initListData();
// 读取后立即删除,避免缓存污染和数据泄露
uni.removeStorageSync('expandedListParams');
}
}
注意:该方案适合简单场景,不推荐传递大体积数据,且需要手动删除临时存储,否则会占用小程序缓存空间。
五、总结
- uni-app 小程序(兼容鸿蒙)传递多参数,优先使用
eventChannel事件通道,完胜 URL&拼接。 - 核心避坑点:接收页在
onLoad生命周期中调用this.getOpenerEventChannel(),确保事件通道获取成功。 eventChannel支持任意数据类型、无需处理特殊字符、兼容性更好,是 uni-app 页面多参数传递的最优解。