# 1. 不开启 deep
1. 不开启 deep 情况下 (默认 watch 只会监听数据本身,而内部数据改变不会监听)
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>not-deep-watch</title> | |
</head> | |
<body> | |
<div id="app"></div> | |
<template id="my-app"> | |
<div> | |
<h2>{{ info }}</h2> | |
<button @click="changeInfo">改变info</button> | |
<button @click="changeInfoName">改变info.name</button> | |
<button @click="changeInfoFriendName">改变 | |
info.friend.name</button> | |
<button @click="changeInfoArray">改变info.array</button> | |
</div> | |
</template> | |
<!-- 测试记得引用 vue.js --> | |
<script src="./vue.js"></script> | |
<script> | |
const App = { | |
template: '#my-app', | |
data() { | |
return { | |
info: { | |
name: 'aimer', | |
age: 16, | |
friend: { name: 'Neko', age: 16 }, | |
array: [1, 2, 3] | |
} | |
} | |
}, | |
watch: { | |
// 默认情况下 watch 只会监听数据本身,而内部数据改变不会监听 | |
info(newInfo, oldValue) { | |
// 模板字符串方式在 control 看不到具体效果 | |
//console.log (`新值:${newInfo}, 旧值:${oldValue}`); | |
console.log('新值:', newInfo, '旧值:', oldValue); | |
} | |
}, | |
methods: { | |
// 未启用 deep 模式下 点击这几个按钮 | |
changeInfo() { | |
// 直接将 info 重新赋值 原数据改变 watch 会触发 | |
this.info = 123 // 结果参考 result1 | |
}, | |
changeInfoName() { | |
// 给 info 里面的属性 name 重新赋值,不会触发 watch | |
this.info.name = 'Neko' // 结果参考 result2 | |
}, | |
changeInfoFriend() { | |
// 给 info 里面的属性 对象类型 (friend) 重新赋值 不会触发 watch | |
this.info.friend.name = 'Aimer' // 结果参考 result3 | |
}, | |
changeInfoArray() { | |
// 给 info 里面的属性 array 添加元素 不会触发 watch | |
this.info.array.push('123') // 结果参考 result4 | |
} | |
} | |
} | |
Vue.createApp(App).mount('#app'); | |
</script> | |
</body> | |
</html> |
结果如下:
数据未发生改变前原图:
result1:
result2:
result3:
result4:
# 2. 开启 deep
- 在开启 deep 情况下 (watch 无论基本或复杂数据类型都会监听)
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>deep_watch</title> | |
</head> | |
<body> | |
<div id="app"></div> | |
<template id="my-app"> | |
<div> | |
<h2>{{ info }}</h2> | |
<button @click="changeInfo">改变info</button> | |
<button @click="changeInfoName">改变info.name</button> | |
<button @click="changeInfoFriendName">改变info.friend.name</button> | |
<button @click="changeInfoArray">改变info.array</button> | |
</div> | |
</template> | |
<script src="./vue.js"></script> | |
<script> | |
const App = { | |
template: '#my-app', | |
data() { | |
return { | |
info: { | |
name: 'aimer', | |
age: 16, | |
friend: { name: 'Neko', age: 16 } | |
} | |
} | |
}, | |
watch: { | |
//deep 为 true 情况下 watch 会监听数据本身,而内部数据改变也会监听 | |
info: { | |
handler: function (newInfo, oldValue) { | |
console.log('新值:', newInfo, '旧值:', oldValue); | |
}, | |
deep: true // 深度监听 | |
}, | |
}, | |
methods: { | |
// 启用 deep 模式下 点击这几个按钮 | |
changeInfo() { | |
// 直接改变 info watch 会触发 | |
this.info = 123 // 结果参考 result1 | |
}, | |
changeInfoName() { | |
this.info.name = 'Neko' // 会触发 watch 结果参考 result2 | |
}, | |
changeInfoFriendName() { | |
// 给 info 里面的 对象 (friend) 里面的 name 重新赋值会触发 watch | |
this.info.friend.name = 'Aimer' // 结果参考 result3 | |
}, | |
// 给 info 里面的数组 添加元素时 会触发 watch | |
changeInfoArray() { | |
this.info.array.push('123') // 结果参考 result4 | |
} | |
} | |
} | |
Vue.createApp(App).mount('#app'); | |
</script> | |
</body> | |
result1:![watch](deep_result1.png) | |
</html> |
结果如下:
数据未发生改变前原图:
result1⇩:注意旧值与新值完全改变,是可以拿到不一样的新值和旧值
result2⇩:注意旧值与新值的 name 属性,却是完全一样的,也就是说拿不到旧值了,拿到的旧值也是和新值一样的数据了,这是因为他们的指向引用地址是一样的!如果想取旧值可能需要其他操作,例如先拷贝一份!请参考原图对比
result3⇩:此时你已经发现了不对劲,和 result2 的结果类似,info.friend 里面的 name 属性新值与旧值也是一样的 请参考原图对比
result4⇩:同理!请参考原图对比
# 3. 结论
-
在未开启 deep 时,除非直接修改 info (例:info = ???) ,其余任何内部修改属性或赋值都不会监听
-
在开启 deep 时,无论是修改其内部基本数据类型或复杂数据类型,修改属性或赋值都会被监听。而且在修改了内部的属性,新值与旧值是一致的,是因为他是引用类型,他们的引用也都是指向同一个对象的,这就涉及了浅拷贝的相关知识!
# 4.immediate
1.immediate 就是不需要 API 操作,数据是否有变化,侦听的函数都会有限执行一次,也就是希望一开始就会立即执行一次 watch
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>immediate_watch</title> | |
</head> | |
<body> | |
<div id="app"></div> | |
<template id="my-app"> | |
<div> | |
<h2>{{ info }}</h2> | |
</div> | |
</template> | |
<script src="./vue.js"></script> | |
<script> | |
const App = { | |
template: '#my-app', | |
data() { | |
return { | |
info: { | |
name: 'aimer', | |
age: 16, | |
friend: { name: 'Neko', age: 16 } | |
} | |
} | |
}, | |
watch: { | |
info: { | |
handler: function (newInfo, oldValue) { | |
console.log(`新值:${newInfo}, 旧值:${oldValue}`); | |
}, | |
deep: true, // 深度监听 | |
immediate: true // 立即执行 | |
}, | |
}, | |
} | |
Vue.createApp(App).mount('#app'); | |
</script> | |
</body> | |
</html> |
结果如下:
result:
默认执行一次旧值为 undefined,是因为没有对 info 进行任何操作就执行了~
# 5. 监听对象里的某个属性(deep: true)
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Document</title> | |
</head> | |
<body> | |
<div id="app"></div> | |
<template id="my-app"> | |
<div> | |
<h2>{{ info }}</h2> | |
<button @click="changeInfo">改变info</button> | |
<button @click="changeInfoName">改变info.name</button> | |
<button @click="changeInfoFriendFriendName">改变info.friend.friend.name</button> | |
<button @click="changeInfoFriendFriendArray">改变info.friend.friend.array</button> | |
</div> | |
</template> | |
<script src="./vue.js"></script> | |
<script> | |
const App = { | |
template: '#my-app', | |
data() { | |
return { | |
info: { | |
name: 'aimer', | |
age: 16, | |
friend: { name: 'Neko', age: 16, friend: { name: 'Annie', array: [1, 2, 3] } }, | |
}, | |
} | |
}, | |
watch: { | |
"info.friend": { | |
handler: function (newName, oldName) { | |
console.log('新值:', newName, '旧值:', oldName); | |
}, | |
deep: true | |
} | |
}, | |
methods: { | |
changeInfo() { | |
// 此时肯定是可以监听到的 参考 result1 | |
this.info = 123 | |
}, | |
changeInfoName() { | |
// 此时是监听不到的 参考 result2 | |
this.info.name = 'Neko' | |
}, | |
changeInfoFriendFriendName() { | |
// 此时也是可以监听到的 参考 result3 | |
this.info.friend.friend.name = 'Alison' | |
}, | |
changeInfoFriendFriendArray() { | |
// 此时也是可以监听到的 参考 result4 | |
this.info.friend.friend.array.push('123') | |
} | |
} | |
} | |
Vue.createApp(App).mount('#app'); | |
</script> | |
</body> | |
</html> |
结果如下:
数据未发生改变前原图:
result1⇩:此时是监听到变化的,但是拿不到新值。是因为改变的是整个 info,而不是 info.friend 里面的属性,所以有监听到变化,但是 info.friend 里面已经不存在值,故为 undefined
result2⇩:因为改变的不是所监听的属性,故无反应 请参考原图对比
result3⇩:新值与旧值与上述也是一样的 请参考原图对比
result4⇩:同理! 请参考原图对比
至于没有开启deep情况下和上面类似,也可以自行去测!另外补充一下,created里面watch可以有返回值(函数),调用函数可以取消!
created () { | |
const unwatch = this.$watch ("info",function (newInfo, oldInfo) { | |
console.log (newInfo, oldInfo); | |
}, { | |
deep: true, | |
immediate: true | |
}) | |
console.log (unwatch); // 输出是箭头函数 | |
//unwatch () // 取消 | |
} | |
} |