一次 vue2 Watch 监听失效引发的对 Watch 的思考
bug 场景
先来说说 bug 遇到的场景,项目是 vue2 + ts ,一个简单的父子组件通信,传递的是一个对象,然后我需要监听对象中的某个属性,当他变化时,我需要做一些处理。类似下面的代码
father.vue
<template>
<Child :pen="obj"/>
</template>
···省略部分代码
child.vue
···省略部分代码
<script>
export default class Child extends Vue {
@Watch('pen.dropDown')
onWatchList(val, oldVal) {
···
}
};
</script>
···省略部分代码
但是我发现有时候 watch 并没有起作用,也就是我传递的数据坚挺的那个属性变了,但是并没有做处理。后来分析定位 bug 的时候,发现当在构建 child 实例的时候,传递的对象里边没有 dropDown 属性,后来传递的对象有 dropDown 属性,但是 watch 回调并没有执行。
源码分析
vue\src\core\instance\state.js 49行, 在实例化组件的时候会初始化 watch,只有这一次机会 。
export function initState (vm: Component) {
···
if (opts.watch && opts.watch !== nativeWatch) {
initWatch(vm, opts.watch)
}
}
vue\src\core\instance\state.js 293行,初始化 watch 是用 for in
遍历组件的 watch 配置,并调用 createWatcher 方法处理。并且会判断坚挺的对象是不是数组类型,如果是数组类型需要遍历数组对每个元素调用 createWatcher 方法处理。
function initWatch (vm: Component, watch: Object) {
for (const key in watch) {
const handler = watch[key]
if (Array.isArray(handler)) {// 判断是否是数组
for (let i = 0; i < handler.length; i++) {
createWatcher(vm, key, handler[i])
}
} else {
createWatcher(vm, key, handler)
}
}
}
vue\src\core\instance\state.js 306行,这里会调用 vm.$watch
内置方法处理。
function createWatcher (
vm: Component,
expOrFn: string | Function,
handler: any,
options?: Object
) {
if (isPlainObject(handler)) {
options = handler
handler = handler.handler
}
if (typeof handler === 'string') {
handler = vm[handler]
}
return vm.$watch(expOrFn, handler, options)
}
bug 原因猜想
可以看到在初始化的时候是使用for in
遍历 watch 的配置,由于 dropDown 属性不存在会拿到 undefined,所以 watch 实际是监听的 undefined,它不会改变,当你再次传递数据给子组件时,虽然有了 dropDown 属性,但是并没有监听它的回调,所以不会对 dropDown 属性做出处理。
解决方案
方案一
在传数据时处理,保证一定有这个属性,这样就能成功监听该属性。
方案二
监听另一个一定存在的属性,并且要保证该属性的变化与 dropDown 属性的变化是同步的,这样当该属性变化时就可以执行回调,处理 dropDown。
我是孤城浪人,一个正在前端路上摸爬滚打的菜鸟,期待你的关注。
转载自:https://juejin.cn/post/7149762007128866852