uni-app父子组件传值仅支持props(父→子)和$emit(子→父);props需带冒号绑定且子组件必须显式声明,引用类型修改会同步影响父组件;$emit事件名须用短横线,v-model需按规则配对value/input或update:xxx,nvue端不支持v-model。
uni-app 里父子组件传值只有两条主路:父→子靠 props,子→父靠 $emit。别信“ref 调用子组件方法”能替代通信——那只是临时控制行为,不是数据流转,且 nvue 端不支持。
父组件怎么把数据传给子组件:冒号不能少,声明不能漏
绑定必须带冒号,否则传的是字符串字面量::title="parentTitle" ✅,title="parentTitle" ❌。Vue3 版本下 :title="{{parentTitle}}" 已废弃,直接报错或静默失败。
子组件必须显式声明 props,哪怕只写 props: ['title']。不声明就直接在模板里用 {{title}},H5 可能正常,小程序端大概率白屏——各端对未声明 props 的处理策略不一致。
引用类型(如 Object、Array)传入后,子组件内部修改会同步影响父组件数据。这不是 bug,是 JS 引用特性。避免方式:
– 浅拷贝用 Object.assign({}, props.config) 或 [...props.list]
– 深拷贝推荐 lodash.cloneDeep(需 npm 构建引入,小程序不支持 require 直接调用)
– 更稳妥做法:所有修改都通过 $emit 抛出去,由父组件决定是否更新自身 data
子组件怎么把数据回传给父组件:事件名必须用短横线
子组件触发 this.$emit('update-user', payload),父组件监听 @update-user="handler"。这是全平台兼容写法。
别用驼峰:this.$emit('updateUser') ❌ —— 微信小程序会转成小写,父组件得监听 @updateuser 才能收到,极难排查;
也别用下划线:this.$emit('update_user') ❌ —— 某些平台支持不稳定。
子组件内部不要在 data 里定义和 props 同名的变量,比如 props: ['value'] 却又写 data() { return { value: '' } },会导致响应式丢失,Vue 会警告 “Avoid mutating a prop directly”,但 uni-app 某些版本只是静默忽略。
v-model 不是自动生效的,得按规则配对
默认 v-model 只认 value prop 和 input 事件。写 ,子组件必须:
– 声明 props: ['value']
– 触发 this.$emit('input', newValue)
想换 key,比如用 msg,就得显式写:
– 父组件:
– 子组件:props: ['msg'] + this.$emit('update:msg', val)
注意:App 端(nvue)不支持 v-model,必须退回到手动绑定::msg="text" @update:msg="text = $event"。
跨层级或弹窗场景:别硬套 props/$emit,该用 defineExpose 就用
页面 → 自定义弹窗组件这类“非标准父子”关系,props 和 $emit 依然可用,但更清晰的做法是:
– 父页面用 ref 绑定弹窗组件:
– 子组件末尾加 defineExpose({ openPop }),把打开方法暴露出去
– 父页面调用 popRef.value.openPop() 控制弹出
弹窗确认后回传数据,仍走 defineEmits(['confirm-select']) + emit('confirm-select', data)。这套组合在 Vue3 + uni-app 全端原生兼容,比手写 EventBus 或全局 uni.$on 更轻量、更可控。
真正容易被忽略的是:子组件里改了对象属性却没通知父组件,或者用了 v-model 却没配对事件名,这类问题在 H5 看不出异常,一上小程序就白屏或数据不更新——多端一致性不是默认保障,是靠你每处绑定都写对。










