uni-app中watch监听需区分vue2/vue3语法,小程序端必须用deep:true监听ref对象属性变化或精确监听字段,props监听应优先用watcheffect,dom操作须配合nexttick。
直接说结论:uni-app 里用 watch 监听数据,必须分清 vue 2 和 vue 3(即 setup 语法糖)两种写法,且小程序端对响应式对象的监听有隐性限制——不加 deep: true 时,ref 包裹的对象属性变化根本不会触发。
Vue 3 setup 中 watch 基本写法与常见错误
在 <script setup></script> 里,watch 是函数调用,不是对象配置。最容易错的是把「监听对象某个字段」写成直接监听整个 ref:
- ❌ 错误写法(监听整个
userref,但不加deep: true):watch(user, () => {...})—— 修改user.value.name不触发 - ✅ 正确写法(监听具体字段):
watch(() => user.value.name, (newVal) => {...}) - ✅ 或开启深度监听:
watch(user, () => {...}, { deep: true }),但注意性能开销 - ⚠️
immediate: true不是默认行为,要手动加才在绑定时执行一次
监听数组或对象内部变化时 deep 参数不能省
uni-app 小程序平台(尤其是微信、支付宝)对响应式代理的实现比 H5 更保守。比如你有个 ref 数组:
const list = ref([{ id: 1, done: false }])- 执行
list.value[0].done = true—— 默认监听完全不触发 - 必须写成:
watch(list, () => {...}, { deep: true })或更精准地:watch(() => list.value.map(i => i.done), () => {...}) - ⚠️
deep: true会递归遍历整个对象树,大数据量时卡顿明显,别无脑加
父传子 props 变化监听不到?用 watchEffect 更稳
当子组件接收 defineProps 的 fenshuj 字符串,发现 watch(fenshuj, ...) 不触发,大概率是因为:
- 父组件传的是静态值(没响应式),或传的是普通字符串而非
ref/reactive - props 是只读的,Vue 3 不允许直接监听 props 变量(会警告)
- ✅ 推荐改用
watchEffect:watchEffect(() => { console.log('fenshuj changed:', props.fenshuj) }) - ✅ 或先转成
ref:const fenshujRef = ref(props.fenshuj); watch(fenshujRef, ...)
监听后修改 DOM 宽度/样式却无效?nextTick 是必选项
在 watch 回调里直接操作 this.$refs.xxx.style.width 失效,不是代码写错了,而是 DOM 还没更新:
- ❌
watch(myWidth, () => el.style.width = myWidth.value + 'px')—— 时机太早,元素可能还没挂载或刚更新完 - ✅ 必须包一层:
watch(myWidth, () => { this.$nextTick(() => { el.style.width = myWidth.value + 'px' }) }) - ⚠️
this.$nextTick在 Vue 3setup中要从vue导入:import { nextTick } from 'vue',再写nextTick(() => {...}) - 如果元素是条件渲染(
v-if),还得先检查el是否存在
最常被忽略的一点:uni-app 小程序平台下,watch 对非响应式来源(如 props 字符串、plus.push 回调里的裸对象)完全无感,必须手动包裹成 ref 或改用 watchEffect 才能建立依赖追踪。










