使用watchEffect来监听父组件传来的goods
为什么我会用watchEffect,起初我的想法是:我需要生成数据字典并且初始化数据这两个函数当中都会用到很多的goods中的数据,如果使用watch监听要设置岂不是很麻烦?但是其实后面发现watch要更好一点,这两个侦听器里的回调我们只需要调用一次就好(初始化数据),并不需要监听里面的各种数据。
场景
// 初始化状态,可否选中
const initDisabledState = (specs, pathMap) => {
specs.forEach(spec => {
spec.values.forEach(value => {
if(pathMap[value.name]) { // 如果数据字典中有该字段
value.disabled = false
} else { // 数据字典中没有该字段
value.disabled = true
}
})
})
}
watchEffect(() => {
pathMap = props.goods.specs && generatedPathDictionary(); // 生成一个数据字典
props.goods.specs && initDisabledState(props.goods.specs, pathMap) // 初始化disabled
})
以上代码是对我项目中关键性代码的提前。
因为第一次执行的时候还未拿取到数据,所以我在调用这两个函数的时候加了一个判断,避免报错;
上面中的 initDisabledState 函数里面会使用到 props.goods.specs 中的数据,并且会给其中里面的属性添加 disabled 属性并赋值。经过我的测试发现:watchEffect 只会执行两次:第一次是该侦听器的立即执行,第二次是接收到了父组件传来的值出发了 侦听器的回调。此后我们改变 disabled 的值并不会触发侦听器中的回调,这是由于什么原因呢?
为什么不会持续触发监听的原因及论证代码
起初我是考虑了是因为我们给对象中添加了一个新的属性的缘故,以下是我的测试代码来验证是否属实:
// 先定义一个变量,设置了一个arr属性的原因是尽可能模拟上面代码中的场景(详情见源码,此处未列举完毕)
let testObj = reactive({arr: [
{name: 'a'},
{name: 'a'},
{name: 'a'},
]})
watchEffect(() => {
testObj.arr.forEach(item => {
// 原对象中没有该属性
item.age = 12
})
console.log('执行了'); // 控制台中只会打印一次,证明后续改变age的值并没有被监听到
})
setTimeout(() => {
// 更改元素的属性值
testObj.arr[0].age = 111111
}, 2000)
结论:以上代码证明并不是新添加一个属性的缘故
那是否是因为我们对该属性直接进行了一个赋值的行为呢?测试代码如下:
let testObj = reactive({arr: [
{name: 'a'},
{name: 'a'},
{name: 'a'},
]})
watchEffect(() => {
testObj.arr.forEach(item => {
console.log(item.age);
})
console.log('执行了'); // 执行2次,说明:监听到了age的改变
})
setTimeout(() => {
testObj.arr[0].age = 111111
}, 2000)
结论:经过测试发现确实是因为赋值行为,该行为并不会引起监听器(watchEffect,watch)的监听。
但是以上的测试还有点不严谨:是否是因为我们给对象添加了一个新的属性的缘故,所以我有进行了下面的测试:
let testObj = reactive({arr: [
{name: 'a'},
{name: 'a'},
{name: 'a'},
]})
watchEffect(() => {
testObj.arr.forEach(item => {
item.name = 'b'
})
console.log('执行了'); // 依旧值打印一次,说明没有监测name的改变
})
setTimeout(() => {
testObj.arr[0].name = 111
}, 2000)
证明上面的结论确实是正确的。
使用watch来监听父组件传来的goods
watch(() => props.goods.specs, () => {
pathMap = generatedPathDictionary();
initDisabledState(props.goods.specs, pathMap)
})
只在初始化的时候执行一次,就已经ok了。
回顾
刚开始就是因为对项目中的需求没有理解(只需要初始化一次数据就可以)好,并且对watch和watchEffect没有理解好,并且代码中使用watch会有错误,可能是因为我自己使用了深度监听(导致disabled每次改变都要触发侦听器,所以页面每次都要初始化,导致渲染不是预期的那样)的缘故,从而误以为是因为watch不可用。哎,这么简单的事情搞了半天,好歹还是有收获。