一、问题重现
<!– DOM示例 –>
<view v-for="(item,index) in list" :key="item.id || index">
<view @click="goSign(item)">签署协议</view>
</view>
<!– JS示例 –>
goSign(item){
console.log(item) // undefined
}
最近在 uni-app 项目里遇到一个很隐蔽的问题:列表页点击“签署协议”时,方法明明写的是:@click="goSign(item)",正常来说item是可以获取到值的。但是实际在uni-app中goSign函数的item居然是undefined。
二、问题分析
第一反应通常会怀疑事件绑定、数据接口、组件层级,甚至怀疑是不是某个 SDK 影响了运行。但最后排查下来,问题出在一个非常不起眼的地方:
<view v-for="(item,index) in list" :key="item.id || index">
在 H5 / 普通 Vue 里,这种写法一般没问题。item.id || index 会作为表达式正常执行,goSign(item) 也能拿到当前循环项。但 uni-app 编译到微信小程序时,事情不完全一样。微信小程序模板没有 Vue 那种完整的 JS 闭包能力。uni-app 为了支持 v-for 里的事件传参,会在编译阶段生成一套“根据列表和 key 回查原始 item”的逻辑。当我们写:
:key="item.id || index"
uni-app 可能会把它处理成类似:
['list', 'id||index', item.id || index]
也就是说,它没有把 item.id || index 当成一个运行时表达式重新执行,而是把中间的 id||index 当成了字段路径。运行时再去列表里找:
row['id||index']
但真实数据里当然没有一个叫 id||index 的字段,于是回查失败,事件参数里的 item 就变成了 undefined。
三、修复建议
不要在小程序端的 v-for :key 里写复杂表达式,提前把 key 算成一个真实字段:
this.list = result.data.map((item, index) => ({
…item,
rowKey: item.id || index
}))
DOM里改成:
<view v-for="(item, index) in list" :key="item.rowKey">
<view @click="goSign(item)">签署协议</view>
</view>
这次问题的教训是:uni-app 虽然写起来像 Vue,但目标平台是小程序时,模板表达式、事件传参、v-for key 并不总是等价于浏览器里的 Vue 行为。在小程序端,最稳的写法永远是:让 key 指向一个真实、稳定、简单的字段。



