uni-app中watch监听需区分Vue 2与Vue 3 setup写法,小程序端必须加deep: true才能监听ref对象/数组内部变化,props需用watchEffect或转ref监听,DOM操作须包裹nextTick。

uni-app 里用 watch 监听数据,不是“写上就生效”,必须分清 Vue 2 和 Vue 3(setup)两种写法,且小程序端对响应式对象的监听有隐性限制——不加 deep: true 时,ref 包裹的对象属性变化根本不会触发。
Vue 3 setup 中 watch 必须写成函数调用
在 <script setup></script> 里,watch 是导入后调用的函数,不是配置对象。最常错的是把「监听对象某个字段」写成直接监听整个 ref:
- ❌ 错误写法(监听整个
user,但没加deep: true):watch(user, () => {})—— 修改user.value.name不触发 - ✅ 正确写法(监听具体字段):
watch(() => user.value.name, (newVal) => {}) - ✅ 或开启深度监听:
watch(user, () => {}, { deep: true }),但注意性能开销
监听对象/数组内部变化必须加 deep: true
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(() => { console.log('fenshuj changed:', props.fenshuj) }) - ✅ 或先转成
ref:const fenshujRef = ref(props.fenshuj); watch(fenshujRef, )
watch 回调里操作 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' }) })
深层嵌套对象监听、props 变化捕获、DOM 更新时机——这三处最容易漏掉配置或误用 API,一漏就静默失效,调试时得盯住控制台警告和平台差异。


















