为什么要说监视属性呢?主要是因为在 Vue2 的官方文档中并没有详细的去介绍监视属性,但是我觉得监视属性这个东西多少还是有点用的。在官方文档中监视属性被叫做侦听器。
监视(动词):指从一旁监察注视的意思。
属性(名词):事物所具有的不可缺少的性质。如,昼伏夜出,是夜行动物的属性。
在 Vue 中监视属性主要是监视页面中数据状态的变化,并且可以根据数据状态的不同而做出不同的处理。
监视属性在 Vue 中有两种写法,分别是完整形式和简写形式。完整形式写起来相对复杂,但是灵活性更高,可以做一些复杂的处理。简写形式写起来相对简单,但是灵活性差,适合做一些简单的处理。
监视属性的完整形式写法
在 Vue 中每个需要监视的属性都要在 watch 这个配置项中定义,定义的监视属性完整形式是一个键值对。键是需要监视的属性名,值是一个对象。
比如在下面的代码中,data 配置项中的 isSunny 就是一个属性,我要监视这个属性,那就在 watch 配置项中按照特定的格式编写代码。
// 监视属性配置项
watch: {
// 监视isSunny属性
isSunny: {
// handler函数名称是固定的,不能随意更改
handler(newValue, oldValue) {
console.log("new:"newValue, "old:"oldValue)
}
}
}
完整代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="./vue.js"></script>
<title>监视属性</title>
</head>
<body>
<div id="root">
<h2>
今天是{{ isSunny ? "晴天,适合出去玩" : "阴天,出门记得带伞!" }}
</h2>
<button @click="changeWeather">切换天气</button>
</div>
<script>
Vue.config.productionTip = false
new Vue({
el: "#root",
data: {
isSunny: true
},
// 方法配置项
methods: {
// 按钮点击触发此方法
changeWeather() {
this.isSunny = !this.isSunny
}
},
// 监视属性配置项
watch: {
// 监视isSunny属性
isSunny: {
// handler函数名称是固定的,不能随意更改
handler(newValue, oldValue) {
console.log("new:"newValue, "old:"oldValue)
}
}
}
})
</script>
</body>
</html>
代码运行后在浏览器中成功的显示了出来,此时我还没有点击页面中的按钮。因为如果我点击按钮的话,右侧控制台会显示代码的输出内容,而此时控制台是空的。
现在尝试点击两次页面中的按钮,可以看到控制台进行了两次输出,而输出结果正是由 isSunny 这个监视属性中 handler 函数进行输出的。
分析这两次输出的结果
第一次:点击按钮后触发 changeWeather方法,isSunny 属性的值改变了。在监视属性中检测到了 isSunny 值的变化,从而触发 handler 函数,该函数接收两个参数。第一个参数是被监视的属性变化后的值,第二个参数是被监视的属性变化前的值。
isSunny 属性在未点击按钮之前的值是 true,点击按钮之后值变成了 false,所以第一次输出 new: false(新值是 false)old: true(旧值是true)
第二次:逻辑和第一次的一样,之不过值发生了变化。
immediate配置
在监视属性的完整形式中,除了 handler 这个配置项外,还有两个常用的配置项,分别是 immediate 和 deep,先来看 immediate。
immediate 的作用主要是可以让 handler 函数在页面渲染完成之后立即执行,不再需要函数值改变后再执行。immediate 这个单词中文翻译后也有即时、立刻的意思。
// 监视属性配置项
watch: {
// 监视isSunny属性
isSunny: {
// immediate表示在页面渲染完成后立即执行handler函数,默认值为false
immediate: true,
// handler函数名称是固定的,不能随意更改
handler(newValue, oldValue) {
console.log("new:"newValue, "old:"oldValue)
}
}
}
在 isSunny 监视属性中添加 immediate 这个配置然后直接运行程序,注意此时我并没有点击页面中的按钮,但控制台却输出了一条内容,证明 isSunny 监视属性的 handler 函数被 immediate 触发了。
由于页面也只是刚刚渲染完成,所以被监视的 isSunny 属性并没有进行状态变化,那也就很好的解释了控制台输出的 newValue 是 isSunny 的初始值 true,而 oldValue 由于不存在所以默认值为 undefined。
deep配置
deep 配置项在监视属性中起到深度监视的作用,默认值为 false。意思是可以监视多层次的数据,比如对象和数组这种多层次的数据结构。如果监视属性中不添加 deep 配置,恰好这个属性又是一个对象类型的数据,那么在修改对象中任何一个值的时候,都不会触发监视属性中的 handler 函数。
接下来在原来的代码中添加一些代码来看看 deep 这个配置项的作用
在 DOM 结构中添加
<h2>深度监视</h2>
<h3>x的值是: {{ testNum.x }}</h3>
<button @click="testNum.x++">点我让x+1</button>
在 data 配置项中添加
data: {
testNum: {
x: 0
}
}
在 watch 配置项中添加
// 监视属性
watch: {
// 监视testNum属性
testNum: {
handler(newValue, oldValue) {
console.log("testNum-------x被改变了")
}
},
// 监视testNum属性中的x属性
x: {
handler(newValue, oldValue) {
console.log("x------x被改变了")
}
}
}
浏览器运行结果
可以看到我已经点击四次按钮了,x 的值也已经改变了,按理说应该触发 testNum 或 x 中的 handler 函数,但控制台并没有输出,也就表示这两个中的任何一个 handler 函数都没有被触发。
说说为什么,先来说说上图代码中 watch 配置项中最后一个 x 的配置对象,我就直接说了,这个配置对象根本就不可能被执行,因为 watch 只能监视在 data 中直接定义的属性,而 x 是属于在 testNum 中定义的值,Vue 在 data 中根本找不到 x 这个属性,所以 Vue 就不会管这个配置对象,就更别说触发 handler 函数了。如果想单独监视 testNum 中 x 这个值是否被更改,那就要将配置对象写成这样"testNum.x":{......}
,更改x值时就可以被监视到了。
可以看到点击按钮后触发点击事件,x的值加一并且"testNum.x"
中的 handler 函数被触发,控制台输出了相应的内容。
再来说说 watch 中第一个"testNum"
的配置对象,我在页面中是让 testNum 里的 x 值进行加一,由于 watch 默认只能监视到属性本身是否发生了变化,那么无论怎么更改 testNum 中 x 的值,testNum 本身都是不会变化的,除非给 testNum 重新赋值一个新的对象,那么此时 Vue 是会监视到 testNum 变化的。对于一个对象来说,如果想在修改其中的值时就能被监视到,那就必须在属性的配置对象中添加 deep 这个配置项,并且要将 deep 的值指定为 true,具体用法和 immediate 一样。修改之后再来看看控制台是否会输出相应的内容。
在监视属性中添加deep属性
// 监视testNum属性
testNum: {
// 深度监视,主要用来监视多层结构的对象或数组
handler(newValue, oldValue) {
console.log("testNum-------x被改变了")
}
},
在 testNum 属性配置对象中添加 deep 之后,在页面中增加 x 的值,控制台输出相应的内容了,那就说明 testNum 中的 handler 函数被触发了。
监视属性的简写形式
在定义某个监视属性时,如果不需要 immediate、deep 等配置,那就完全可以将监视属性简写成函数式。
watch: {
// 直接将属性简写成函数,这个函数就是之前的handler函数
isSunny(newValue, oldValue) {
console.log("new: ", newValue, "old: ", oldValue)
}
}
总结
当被监视的属性发生变化时,回调函数handler会被自动调用并进行相应的操作。
所要监视的属性必须存在,才能进行监视,否则不会有效果,控制台也不会报错。
深度监视:
Vue中的watch默认不监测对象内部值的改变。
配置deep:true就可以检测到对象内部值的改变了
注意:
在使用watch监测数据是要根据数据的具体结构,再决定是否使用深度监视。
Vue自身可以监测对象内部值的改变,但由Vue提供的watch默认不行。