监听器

监听器

Vue提供了一种更通用的方式来观察和响应Vue实例上的数据变动:监听器。当有一些数据需要随着其他数据变化而变化时就可以使用监听器。

使用监听器

监听器在Vue实例的选项对象的watch中定义,下面用监听器实现千米和米的换算:

<html>
	<head>
		<meta charset="UTF-8">
		<title>监听器</title>
	</head>
	<body>
		<div id = "app">
		   千米 : <input type = "text" v-model="kilometers">
		   米 : <input type = "text" v-model="meters">
		</div>
		<script src="https://unpkg.com/vue@next"></script>
		<script>
			const vm = Vue.createApp({
    		    data() {
    		        return {
    		            kilometers: 0,
    		            meters: 0
    		        }
    		    },
    		    watch: {
    	            kilometers(val) {
    	                this.meters = val * 1000;
    	            },
	                // 监听器函数也可以接受两个参数,val是当前值,oldVal是改变之前的值
    	            meters(val, oldVal) {
    	                this.kilometers = val / 1000;
    	            }
		        }
		    }).mount('#app');
		</script>
	</body>
</html>

我们编写了两个监听器,分别监听数据属性kilometers和meters的变化,当其中一个数据发生变化时,对应的监听器就会被调用,经过计算得到另一个数据属性的值。

当需要在数据变化时执行异步或开销较大的操作时,使用监听器是最合适的。例如在一个在线问答系统中,用户输入的问题需要到服务端获取答案,就可以考虑对问题属性进行监听,在异步请求答案的过程中,可以设置中间状态,向用户提示“请稍后。。。”,而这样的功能使用计算属性就无法做到。

下面用监听器实现菲波那契数列,斐波那契数列的计算比较耗时,我们采用HTML5新增的WebWorker来计算。

function fibonacci(n){
    return n < 2 ? n :arguments.callee(n-1) + arguments.callee(n-2);
}

onmessage = function(event){
    var num = parseInt(event.data,10);
    postMessage(fibonacci(num));
}

接下来是主页面:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title>监听器</title>
	</head>
	<body>
		<div id = "app">
		   请输入要计算斐波那契数列的第几个数:
			<input type="text" v-model="num">
			<p v-show="result">{{result}}</p>
		</div>
	
		<script src="https://unpkg.com/vue@next"></script>
		<script>
			const vm = Vue.createApp({
    		    data() {
    		        return {
    		            num: 0,
    		            result: ''
    		        }
    		    },
				watch: {
				    num(val) {
    				  	this.result = "请稍候...";
    				  	if(val > 0){
    				    	const worker = new Worker("fibonacci.js");
    				    	worker.onmessage = (event) => this.result = event.data;
    						worker.postMessage(val);
    					}
    					else{
    						this.result = '';
    					}
				    }
				}
			}).mount('#app');
		</script>
	</body>
</html

说明:斐波那契数列比较耗时所以在25行给result数据属性设置了一个中间状态,给用户一个提示。

worker实例是异步执行的,当后台线程执行完任务后通过postMessage调用通知创建者线程的onmessage回调函数,在该函数中可以通过event对象的data属性得到数据。在这种异步回调的执行过程中,this的指向会发生变化,如果onmessage回调函数写成如下形式:worker.onmessage = function(event){this.result = event.data};,那么在执行onmessage函数时,this实际上指向worker对象,自然this.result就是undefined。如果用箭头函数,它绑定的是父级作用域的上下文,在这里就是vm对象。

本例使用了WebWorker,直接在文件系统打开会报错,解决方法是将本例部署到本地的一个Web服务器中。

监听器的更多形式

监听器在定义时除了直接写一个函数还可以接收一个方法名:

<div id='app'>
    年龄:<input type='text' v-model='age'>
    <p v-if='info'>{{info}}</p>
</div>

<script>
	const vm = Vue.createApp({
        data() {
            return {
                age:0,
                info:''
            }
        },
        methods:{
            checkAge(){
                if(this.age>=18)
                    this.info='已成年';
                else
                    this.info='未成年';
            }
        },
        watch:{
            age:'checkAge'
        }
    }).mount('#app');
</script>

监听器还可以监听一个对象的属性变化:

<div id='app'>
    年龄:<input type='text' v-model='person.age'>
    <p v-if='info'>{{info}}</p>
</div>
<script>
	const vm = Vue.createApp({
        data() {
            return {
                person:{
                    age:0,
                    name:'lisi'
            },
                info:''
        }
        },
        watch:{
            //该回调会在person对象的属性改变时被调用,无论该属性嵌套多深
            person:{
                handler(val,oldVal){
                    if(val.age>=18)
                        this.info = '已成年';
                    else
                        this.info = '未成年';
                }
                deep:true
            }
        }
    }).mount('#app');
</script>

要注意在监听对象属性改变时,使用了handler和deep,前者用于定义当数据变化时调用的监听器函数,后者主要在监听对象属性变化时使用,如果为true,表示无论该对象的属性在对象中的层级有多深,只要该属性的值发生变化都会被监测。

监听器函数在初始渲染时并不会被调用,只有在后续监听的属性发生变化时才会被调用,如果要让监听器函数在监听开始后立即执行,可以使用immediate,将其设为true。例如:

 watch:{
     //该回调会在person对象的属性改变时被调用,无论该属性嵌套多深
     person:{
         handler(val,oldVal){
             if(val.age>=18)
                 this.info = '已成年';
             else
                 this.info = '未成年';
         }
         deep:true,
             immediate:true
     }
 }

当页面加载后就会立即显示未成年。

如果仅仅是监听对象的一个属性变化,且变化也不影响对象的其他属性,那么就没必要这么麻烦,直接监听该对象属性即可:

watch:{
    //因为有特殊字符点号,所以要用引号包裹
    'person.age':function(val,oldVal){
        ...
    }
}

实例方法$watch

除了在watch选项中定义监听属性外,还可以使用组件实例的$watch方法观察组件实例上的响应式属性或计算属性的更改。对于顶层数据属性、prop和计算属性,只能以字符串的形式传递他们的名字。例如:

vm.$watch('kilometers',(newValue,oldValue) => {
    //这个回调将在vm.kilometers改变后调用
    document.getElementById('info').innerHTML = "修改前的值为:" + oldValue + ",修改后的值:" + newValue;
})

对于更复杂的表达式或嵌套属性,则需要使用函数形式:

const app = Vue.createApp({
    data(){
        return {
            a:1,
            b:2,
            c:{
                d:3,
                e:4
            }
        }
    },
    created(){
        //顶层属性名
        this.$watch('a',(newVal,oldVal) => {
            //do something
        })
        //用于监听单个嵌套属性的函数
        this.$watch(
        () => this.c.d,
        (newVal,oldVal) => {
            //do something
        })
        //用于监听复杂表达式的函数
        this.$watch(
        //每次表达式“this.a+this.b”产生不同结果时
        //都会调用处理程序
        () => this.a + this.b,
        (newVal,oldVal) => {
            //do something
        })
    }
})

$watch方法还可以接收一个可选的选项对象作为其第三个参数。例如。要监听对象内部的嵌套属性的变化时,可以在选项参数中传入deep:true:

vm.$watch('person',callback,{
    deep:true
})
vm.person.age = 20
//回调函数被触发
posted @ 2021-08-15 16:14  KKKyrie  阅读(72)  评论(0编辑  收藏  举报