目录
- 一、基本使用
- 1.1 watch配置监视
- 1.2 vm.$watch动态监视
- 1.3 深度监视(deep watch)
- 1.4 简写形式
- 二、computed和watch的对比
- 2.1 使用watch实现setTimeout操作
- 2.2 用computed无法实现setTimeout
- 三、其他注意事项
- 3.1 vue devtools的bug
- 3.2 @xxx=yyy格式
- 3.3 将window传入data中
Vue
提供了一种更通用的方式来观察和响应 Vue实例上的数据变动:侦听属性,也成为监视属性
。
一、基本使用
监视属性watch
:
- 当被监视的属性变化时,回调函数
handler
自动调用,进行相关操作。 - 监视的属性必须存在,才能进行监视。
- 监视的两种写法
a.new Vue
时传入watch
配置
b. 通过vm.$watch
来动态实现监视
1.1 watch配置监视
下面案例中,我们通过watch
配置属性来给isHuoguo
添加监视,当isHuoguo
发生变化时,所配置的handler()
函数会调用。
注意:这里有一个属性immediate
,其默认值为false
。当配置为true
时,页面初始化时,让handler
调用一下。
<div id="root"><h2>今天我们去吃{{info}}</h2><button @click="change">切换</button>
</div>
<script>const vm = new Vue({el:'#root',data:{isHuoguo:true},computed:{info() {return this.isHuoguo?'火锅':'南京大排档';}},methods: {change() {this.isHuoguo = !this.isHuoguo;}},watch: {isHuoguo:{immediate:true,//默认为false,初始化时,让handler调用一下。handler(newValue, oldValue) {console.log("isHuoguo属性被修改了,newValue:"+newValue+",oldValue:"+oldValue);}}}})
</script>
效果图如下:
1.2 vm.$watch动态监视
不仅可以给属性
(即data
中定义的)添加监视,也可以给计算属性
添加监视。
下面例子,我们通过vm.$watch
方式,来动态给计算属性info
添加监视:
<div id="root"><h2>今天我们去吃{{info}}</h2><button @click="change">切换</button>
</div>
<script>const vm = new Vue({el:'#root',data:{isHuoguo:true},computed:{info() {return this.isHuoguo?'火锅':'南京大排档';}},methods: {change() {this.isHuoguo = !this.isHuoguo;}},watch: {isHuoguo:{// immediate:true,//默认为false,初始化时,让handler调用一下。handler(newValue, oldValue) {console.log("isHuoguo属性被修改了,newValue:"+newValue+",oldValue:"+oldValue);}}}})// 监视计算属性vm.$watch('info',{// immediate:true,//默认为false,初始化时,让handler调用一下。handler(newValue, oldValue) {console.log("info计算属性被修改了,newValue:"+newValue+",oldValue:"+oldValue);}})</script>
1.3 深度监视(deep watch)
深度监视(deep watch
):
Vue
中的watch
默认不监测对象内部值的改变(只监测一层结构)- 配置
deep:true
可以监测对象内部值的改变(可以监测多层结构),Vue
默认不开启deep
,是为了提供效率。 Vue
自身可以监测对象内部值的改变,但是Vue
提供的watch
默认不可以。- 使用
watch
时,要根据数据的具体结构,决定是否采用深度监视。
使用方式:
- 监视多级结构中某个属性的变化,例如下面例子中
numbers.a
- 监视多级结构中所有属性的变化,
numbers:{deep:true,handler(){}}
<div id="root"><h3>今天我们去吃{{info}}</h3><button @click="change">切换</button><hr><h3>a的值是{{numbers.a}}</h3><button @click="numbers.a++">点击a++</button><hr><h3>b的值是{{numbers.b}}</h3><button @click="numbers.b++">点击b++</button>
</div>
<script>const vm = new Vue({el:'#root',data:{isHuoguo:true,numbers:{a:1,b:1}},computed:{info() {return this.isHuoguo?'火锅':'南京大排档';}},methods: {change() {this.isHuoguo = !this.isHuoguo;}},watch: {isHuoguo:{handler(newValue, oldValue) {console.log("isHuoguo属性被修改了,newValue:"+newValue+",oldValue:"+oldValue);}},// 监视多级结构中某个属性的变化"numbers.a": {handler() {console.log("a被改变了!");}},// 监视多级结构中所有属性的变化numbers:{deep:true,handler() {console.log("numbers改变了!");}}}})</script>
1.4 简写形式
当所配置的监视,只需要handler
,不需要其他配置的时候,才可以使用简写形式,使用函数来代替。
两类简写形式:
- watch配置里的简写
- 动态添加监视的简写
<div id="root"><h3>今天我们去吃{{info}}</h3><button @click="change">切换</button>
</div>
<script>const vm = new Vue({el:'#root',data:{isHuoguo:true},computed:{info() {return this.isHuoguo?'火锅':'南京大排档';}},methods: {change() {this.isHuoguo = !this.isHuoguo;}},watch: {/* isHuoguo:{deep:true,immediate:true,handler(newValue, oldValue) {console.log("isHuoguo属性被修改了,newValue:"+newValue+",oldValue:"+oldValue);}}, */// 简写形式isHuoguo(newValue, oldValue) {console.log("isHuoguo属性被修改了,newValue:"+newValue+",oldValue:"+oldValue);}}})// 动态添加监视的简写vm.$watch('isHuoguo',function(newValue, oldValue){console.log("isHuoguo属性被修改了,newValue:"+newValue+",oldValue:"+oldValue);})</script>
二、computed和watch的对比
计算属性(computed
)和监视属性(watch
)之间的区别:
computed
能完成的功能,watch
都可以完成watch
能完成的功能,computed
不一定能完成,例如:watch
可以进行异步操作。
两个重要的原则
3. 被Vue
所管理的函数,最好写成普通函数,这样this
的指向才是vm
或组件实例对象
4. 所有不被Vue
管理的函数(定时器的回调函数(setTimeout
)、ajax
的回调函数、Promise
的回调函数等),最好写成箭头函数
,这样this
的指向才是 vm
或 组件实例对象
。
2.1 使用watch实现setTimeout操作
下面代码,使用了watch
实现setTimeout
操作,有一点需要注意的是,setTimeout
所指定的回调函数要使用箭头函数(因为箭头函数本身没有this
),否则this
指向window
了,而不再是vm
实例。
<div id="root"><div class="row">姓:<input type="text" v-model="firstName"></div><div class="row">名:<input type="text" v-model="lastName"></div><div class="row">全名:<span>{{fullName}}</span></div>
</div><script>const vm = new Vue({el:'#root',data() {return {firstName: '小',lastName: '三',fullName:'小-三'};},methods:{},watch:{firstName(val) {setTimeout(() => { //这里不能写成普通函数,否则this指向window了console.log(this); //vm实例对象this.fullName = val + "-" + this.lastName;}, 1000);},lastName(val){this.fullName = this.firstName + "-" + val;}}});
</script>
2.2 用computed无法实现setTimeout
用computed
计算属性,无法实现setTimeout
想要的功能,如下错误代码所示:
<div id="root"><div class="row">姓:<input type="text" v-model="firstName"></div><div class="row">名:<input type="text" v-model="lastName"></div><div class="row">全名:<span>{{fullName}}</span></div></div><script>const vm = new Vue({el:'#root',// 对于Vue来说,data中配置的就是属性。// 计算属性:用现有的属性去加工、计算生成一个全新的属性。和属性分开放data:{firstName: '小',lastName: '三'},computed:{fullName() {console.log('get被调用了!');// console.log(this);setTimeout(() => {return this.firstName+'-'+this.lastName}, 1000);}}});
</script>
三、其他注意事项
3.1 vue devtools的bug
当页面上没有用到某个计算属性时,vue devtools
调试工具会出现一个bug
:不会显示数据的变化了。例如下面代码
<div id="root"><h2>今天我们去吃米饭</h2><button @click="change">切换</button>
</div>
<script>const vm = new Vue({el:'#root',data:{isHuoguo:true},computed:{info() {return this.isHuoguo?'火锅':'南京大排档';}},methods: {change() {this.isHuoguo = !this.isHuoguo;}},})
</script>
点击切换按钮,工具中显示的data
和computed
不发生变化,其实数据已经发生了改变。可以通过控制台中输入vm.info
来查看。如下图所示:
3.2 @xxx=yyy格式
这里的yyy
不是事件名称,而是一些简单的语句。例如:@click="isHuoguo = !isHuoguo;count++;"
下面的案例中,两个按钮均可实现功能。不过需要注意的是,执行语句比较复杂的时候不建议直接写在yyy
中。
<div id="root"><h2>今天我们去吃{{info}}--切换次数{{count}}</h2><button @click="change">切换</button><button @click="isHuoguo = !isHuoguo;count++">切换2</button>
</div>
<script>const vm = new Vue({el:'#root',data:{isHuoguo:true,count:0,},computed:{info() {return this.isHuoguo?'火锅':'南京大排档';}},methods: {change() {this.count++;this.isHuoguo = !this.isHuoguo;}},})
</script>
3.3 将window传入data中
将window
传入data
中,实现alert
弹框。如下代码所示:
<div id="root"><h2>今天我们去吃{{info}}--切换次数{{count}}</h2><button @click="change">切换</button><button @click="isHuoguo = !isHuoguo;count++">切换2</button><button @click="window.alert(1)">弹出alert</button>
</div>
<script>const vm = new Vue({el:'#root',data:{isHuoguo:true,count:0,window //相当于window:window},computed:{info() {return this.isHuoguo?'火锅':'南京大排档';}},methods: {change() {this.count++;this.isHuoguo = !this.isHuoguo;}},})
</script>