• 七、监听器


    本章概要

    • 使用监听器
    • 监听器的更多形式
    • 实例方法 watch

    Vue 提供了一种更通用的方式来观察和响应 Vue 实例上的数据变动:监听器。当有一些数据需要随着其它数据变化而变化时,就可以使用监听器。听起来这和计算属性的作用差不多,从能的描述来看,确实是,不过在实际应用中两者是有很大差别的。

    7.1 使用监听器

    监听器在 Vue 实例的选项对象的 watch 选项中定义。下面通过监听器实现千米和米之间的换算。如下:
    例7-1

    DOCTYPE html>
    <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>
    		<p id="info">p>
    	
    		<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;
        	                console.log("aaa");
        	            },
    	                // 监听器函数也可以接受两个参数,val是当前值,oldVal是改变之前的值
        	            meters(val, oldVal) {
        	                this.kilometers = val / 1000;
        	            }
    		        }
    		    }).mount('#app');
    		script>
    	body>
    html>
    

    编写了两个监听器,分别监听数据属性 kilometers 和 meters 的变化,当其中一个数据属性的值发生改变时,对应的监听器就会被调用,经过计算得到另一个数据属性的值。
    浏览器中的显示效果如下:
    在这里插入图片描述

    注意:不要使用箭头函数定义监听器函数,箭头函数绑定的是父级作用域的上下文,这里的this 指向的是 window 对象而不是组件实例,this.kilometers 和 this.meters 都是 undefined。如下:

    kilometers : (val) =>{
    	this.kilometers = val;
      this.meters = this.kilometers * 1000
    }
    

    当需要在数据变化时执行异步或开销较大的操作时,使用监听器时最合适的。例如,在一个问答系统中,用户输入的问题需要到服务端获取答案,就可以考虑对问题属性进行监听,在异步请求答案的过程中,可以设置中间状态,向用户提示“请稍后…”,而这样的功能使用计算属性就无法做到了。
    下面给出一个使用监听器实现斐波那契数列计算的例子。斐波那契数列的计算比较耗时,采用 HTML5 新增的 WebWorker 来计算,将斐波那契数列的计算放到一个单独的 JS 文件中。如下:
    例7-2

    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));
    }
    

    接下来是主页面,如下:
    例7-3

    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 src="fibonacci.js">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>
    

    说明:
    (1)斐波那契数列的计算比较耗时,所以开始给 result 数据属性设置了一个中间状态,给用户一个提示
    (2)worker 实例时异步执行的,当后台线程执行完成任务后通过 postMessage() 调用,通知创建者线程的 onmessage 回调函数,在该函数中可以通过 event 对象的 data 属性得到数据。在这种异步回调的执行过程中,this 的指向会发生变化,如果 onMessage() 回调函数写成如下形式:

    worker.onmessage = function(event){this.result = event.data};
    

    那么在执行 onmessage() 函数时,this 实际上指向的是 worker 对象,自然 this.result 就是 undefined 。
    因此使用了箭头函数,因为箭头函数绑定的是父级作用域的上下文,在这里绑定的就是 vm 对象。
    在 Vue 的开发中,经常会遇到 this 指向的问题,合理地使用箭头函数可以避免很多问题,后面还会遇到类似的情况。
    运行上述页面,输入一个数,如 40 ,会看到“请稍后…”提示信息,一段时间后会看到斐波那契数列的第40个数。如图:
    在这里插入图片描述

    在这里插入图片描述

    提示:
    Chrome 浏览器的安全限制比较严格,本例使用了 WebWorker ,如果直接使用浏览器打开页面,会提示如下错误信息:
    Uncaught (in promise) DOMException: Failed to construct ‘Worker’: Script at ‘file:///F:/fibonacci.js’ cannot be accessed from origin ‘null’.
    解决办法就是将本例部署到本地的一个 Web 服务器中访问,如 Tomcat 或 nginx 等。

    7.2 监听器的更多形式

    监听器在定义时,除了直接写一个函数外,还可以接一个方法名。如下:

    DOCTYPE html>
    <html>
    
    <head>
    	<meta charset="UTF-8">
    	<title>监听器title>
    head>
    
    <body>
    	<div id="app">
    		年龄: <input type="text" v-model="age">
    		<p v-if="info">{{info}}p>
    	div>
    
    	<script src="https://unpkg.com/vue@next">script>
    	<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>
    body>
    
    html>
    

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

    DOCTYPE html>
    <html>
    
    <head>
    	<meta charset="UTF-8">
    	<title>监听器title>
    head>
    
    <body>
    	<div id="app">
    		年龄: <input type="text" v-model="person.age">
    		<p v-if="info">{{info}}p>
    	div>
    
    	<script src="https://unpkg.com/vue@next">script>
    	<script>
    		const vm = Vue.createApp({
    			data() {
    				return {
    					person: {
    						name: 'lisi',
    						age: 0
    					},
    					info: ''
    				}
    			},
    			watch: {
    				// 该回调会在person对象的属性改变时被调用,
    				// 无论该属性被嵌套多深
    				person: {
    					handler(val, oldVal) {
    						if (val.age >= 18)
    							this.info = '已成年';
    						else
    							this.info = '未成年';
    					},
    					deep: true,
    				}
    			}
    		}).mount('#app');
    	script>
    body>
    
    html>
    

    要注意在监听对象属性改变时,使用了两个新的选项:handler 和 deep,前者用于定义当数据变化时调用的监听器函数,后者主要在监听对象属性变化时使用,如果该选项的值 为 ture ,表示无论该对象的属性在对象的层级有多深,只要该属性的值发生变化,都会被检测到。
    监听器函数在初始渲染时并不会被调用,只有在后续监听的属性发生变化时才会被调用。
    如果要让监听器函数在监听开始后立即执行,可以使用 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){
        ...
      }
    }
    

    注意:
    监听对象属性时,因为有特殊字符号“.”,因此要使用一对单引号或双引号将其包裹起来。

    7.3 实例方法 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 方法还可以接受一个可选的选项对象作为其第 3 个参数。例如要监听对象内部的嵌套属性的变化时,可以在选项参数中传入 deep:true。如下:

    vm.$watch('person',calback,{
      deep:true
    })
    vm.person.age = 20
    // 回调函数被触发
    
  • 相关阅读:
    基于spring+jsp+mysql实现的Java web论坛系统【源码+数据库+指导运行】
    【马蹄集】第二十四周——高精度计算专题
    FlexPro软件:生产、研究和开发中的测量数据分析
    系统移植 DAY4(FSMP1A开发板开发阶段部署)
    U-Mail邮件系统安全登录解决方案
    谈一谈工作中的前后端功能开发范围
    让el-input与其他组件能够显示在同一行
    Centos下Ambari2.7.5的编译和安装
    java毕业设计校园二手交易网站源码+lw文档+mybatis+系统+mysql数据库+调试
    OM | 电子商务平台中的合约选择:批发合约or代理合约?
  • 原文地址:https://blog.csdn.net/GXL_1012/article/details/127104223