写点什么

精通 Vue.js 系列实例教程 │ Vue 组件的数据监听

作者:TiAmo
  • 2023-05-09
    江苏
  • 本文字数:4203 字

    阅读完需:约 14 分钟

精通Vue.js系列实例教程 │ Vue组件的数据监听

简介: 如果 Vue 组件的一个变量 num 会被频繁更新,并且当变量 num 每次被更新时,需要进行一系列耗时的操作,如访问远程服务器的资源,或者通过复杂耗时的运算更新那些依赖变量 num 的其他变量(如 result 变量)。在这种情况下,可以通过 Vue 框架的数据监听器 Watcher 实现对变量 num 的监听。


如果 Vue 组件的一个变量 num 会被频繁更新,并且当变量 num 每次被更新时,需要进行一系列耗时的操作,如访问远程服务器的资源,或者通过复杂耗时的运算更新那些依赖变量 num 的其他变量(如 result 变量)。在这种情况下,可以通过 Vue 框架的数据监听器 Watcher 实现对变量 num 的监听。Vue 的 watch 选项会通过 Watcher 监听数据。例程 1 的 mywatch.html 演示了 watch 选项的基本用法。

■ 例程 1 mywatch.html

<div id="app">     <p><input v-model="num" /></p>     <p>{{ result }}</p>   </div>
<script> const vm=Vue.createApp({ data(){ return{ num: 0, result: 0 } }, methods:{ //睡眠方法,参数numberMillis是睡眠的毫秒数 sleep(numberMillis) { var now = new Date(); var exitTime = now.getTime() + numberMillis while (true) { now = new Date() if (now.getTime() > exitTime) return; } } }, watch: { //当num变量被更新,就会调用此函数 //newNum参数表示更新后的num变量 //oldNum参数表示更新前的num变量 num(newNum, oldNum) { //num变量的监听函数 this.sleep(2000) //睡眠2秒 this.result=Math.sqrt(newNum) //计算平方根 } } }).mount('#app')</script> </body></html>
复制代码

在 mywatch.html 中,Vue 应用实例的 watch 选项中有一个 num()函数,负责监听 num 变量。当 num 变量被更新,Vue 的数据监听器就会调用这个 num()函数。

通过浏览器访问 mywatch.html,会得到如图 1 所示的网页。在网页的 num 变量的输入框输入新的数字,Vue 的数据监听器就会调用 num()函数,更新 result 变量的值。


1、用 Web Worker 执行数据监听中的异步操作

对于例程 1 的 mywatch.html,当用户在网页的 num 变量的输入框输入新的数字时,num()函数就会被 Vue 的数据监听器调用。num()函数会先调用 sleep(2000)方法睡眠 2 秒,通过这种睡眠的方式来模拟耗时的操作。

num()函数是由浏览器的负责执行 JavaScript 脚本的主线程来执行的。当主线程执行 sleep(2000)方法睡眠时,网页处于卡死状态,不能响应用户的任何操作。只有当主线程执行完 num()函数,重新更新了网页,网页才能继续响应用户的操作。

如果希望用户始终可以和网页进行顺畅地交互,不会出现网页卡死的情况,可以通过一个额外的线程异步执行耗时的操作。这里会利用 HTML5 中的 Web Worker 线程来执行耗时操作。

首先创建一个 longtask.js 文件(文件名可以任意取),参见例程 2,它的 onmessage 函数包含了 Worker 线程接收到主线程发送的数据时所执行的操作。

■ 例程 2 longtask.js

//睡眠函数,参数numberMillis是睡眠的毫秒数function sleep(numberMillis) {…}
//当Worker线程接收到主线程发送的数据时,调用此函数onmessage=function(event){ var num=event.data //读取主线程发送过来的数据 sleep(2000) //睡眠2秒,模拟耗时的操作 var result=Math.sqrt(num) //求平方根 postMessage(result) //向主线程发送运算结果 }
复制代码

在 onmessage 函数中,event.data 表示主线程发送过来的 num 变量。postMessage(result)用于向主线程发送 result 变量。

例程 3 的 mywatch-async.html 会通过 Worker 线程执行耗时操作。

■ 例程 3 mywatch-async.html

<div id="app">      <p><input v-model="num" /></p>      <p>{{ result }}</p>    </div>
<script> const vm=Vue.createApp({ data(){ return{ num: 0, result: 0 } } ,
watch: { // 当num变量被更新,就会调用此方法 num(newNum, oldNum) { // num变量的监听函数 this.result='正在运算,请稍后...' //创建Worker线程 var worker=new Worker('longtask.js')
//注册监听接收Worker线程发送数据的函数 worker.onmessage=(event)=>this.result=event.data
//向Worker线程发送数据 worker.postMessage(newNum) } } }).mount('#app')</script>
复制代码

在 num()函数中,浏览器的主线程先通过以下语句为 result 变量赋予一个临时取值:

this.result='正在运算,请稍后...'
复制代码

接着主线程通过“new Worker('longtask.js')”语句创建了 Worker 线程。接下来执行以下语句注册用于监听接收数据的 onmessage()函数:

worker.onmessage=(event)=>this.result=event.data
复制代码

当主线程接收到 Worker 线程发送的数据时,就会执行 worker.onmessage()函数中的 this.result=event.data 语句,event.data 表示 Worker 线程发送的数据。

主线程接着向 Worker 线程发送 newNum 变量:

worker.postMessage(newNum)
复制代码

各个浏览器对 Web Worker 的支持程度不一样。如果在 Chrome 浏览器中访问本地的 mywatch-async.html,然后在网页的输入框修改 num 变量的值,浏览器会产生以下错误:

Uncaught (in promise) DOMException: Failed to construct 'Worker': Script at 'file:///C:/vue/sourcecode/chapter03/longtask.js' cannot be accessed from origin 'null'.
复制代码

这是因为 Chrome 出于安全的原因,不允许使用本地的 Web Worker 线程。把该范例发布到了 JavaThinker.net 网站上,网址如下:

www.javathinker.net/vue/mywatch-async.html
复制代码

通过浏览器访问上述网址,就可以正常访问 mywatch-async.html。在网页的输入框修改 num 变量的值,网页不会卡死,主线程会先显示 result 变量的临时取值,参见图 2。过 2 秒后,主线程再显示由 Worker 线程运算得到的 result 变量。


■ 图 2 网页显示 result 变量的临时取值


图 3 展示了主线程和 Worker 线程的通信及交换数据的过程。


■ 图 3 主线程和 Worker 线程的通信以及交换数据的过程


从图 3 可以看出,当主线程通过 worker.postmessage(newNum)方法向 Worker 线程发送 newNum 变量,就会触发 Worker 线程执行 longtask.js 中的 onmessage()函数。当 Worker 线程通过 postmessage(result)方法向主线程发送 result 变量,就会触发主线程执行 worker.onmessage()函数。无论是主线程还是 Worker 线程,都可以通过 event.data 读取对方发送的数据。

2、在 watch 选项中调用方法

在 watch 选项中还可以调用方法。例如在例程 4 的 score.html 中,如果 score 变量被更新,Vue 的数据监听器就会调用 judge()方法。

■ 例程 4 score.html

 <div id="app">      <p><input v-model="score" /></p>      <p>{{ result }}</p>    </div>
<script> const vm=Vue.createApp({ data(){ return{ score: '', result: '' } } ,
methods:{ judge() { if(this.score>=60) this.result='及格' else this.result='不及格' } },
watch: { score: 'judge' //调用judge()方法 } }).mount('#app')</script>
复制代码

下面对 judge()方法做一些修改,使它通过 JavaScript 语言的 setTimeout()函数执行异步操作。

judge() {              this.result="正在运算,请稍后..."            setTimeout( ()=>{              if(this.score>=60)                this.result='及格'              else                this.result='不及格'            },2000)  //延迟2秒后再执行运算          }
复制代码

以上 judge()方法先给 result 变量赋予了一个临时值“正在运算,请稍后...”,然后利用 setTimeout()函数设置了异步操作:过 2 秒后计算 result 变量的取值。judge()方法产生的运行效果是,网页上首先显示“正在运算,请稍后...”,过 2 秒后再显示 reuslt 变量的实际取值。

3、深度监听

默认情况下,当 Vue 的 watch 选项监听一个对象时,不会监听对象的属性的变化。如果希望监听对象的属性变化,可以在 watch 选项中把 deep 属性设为 true,这样就会支持深度监听。

在例程 5 的 student.html 中,Vue 的 watch 选项会监听 student 对象,由于 deep 属性设为 true,当 student.score 属性被更新,watch 选项中的 handler()函数也会被执行。

■ 例程 5 student.html

<div id="app">      <p><input v-model="student.score" /></p>      <p>{{ result }}</p>    </div>
<script> const vm=Vue.createApp({ data(){ return{ student: { name:'Tom',score: '98'} , result: '' } } ,
watch: { student: { //监听student对象 handler(newStudent,oldStudent){ if(this.student.score>=60) this.result='及格' else this.result='不及格' }, deep: true //启用深度监听 } } }).mount('#app')</script>
复制代码

通过浏览器访问 student.html 网页,在输入框修改 student.score 属性的值,Vue 的数据监听器会调用 handler()函数,更新 result 变量。

当 Vue 的数据监听器深度监听一个对象时,不管对象的属性嵌套了多少层,只要属性发生变化,就会被监听。

4、立即监听

通过浏览器访问例程 5 的 student.html 时,会看到网页上一开始显示{{student.score}}的值为 98,而{{result}}的值为空。因为这时候 Vue 的数据监听器还没有监听到 student.score 属性的变化,因此不会调用 watch 选项中的 handler()函数。

在 Vue 组件的生命周期中,如果希望在它的初始化阶段,Vue 框架就会调用一次 watch 选项中的 handler()函数,为 result 变量赋值,那么可以把 watch 选项的 immediate 属性设为 true。

下面对 student.html 做如下修改,增加 immediate: true 语句。

watch: {  student: {    handler(newStudent,oldStudent){…},    immediate: true,    deep: true    }}
复制代码

再次通过浏览器访问 student.html,会看到网页上{{student.score}}的初始值为 98,{{result}}的初始值为“及格”。

发布于: 刚刚阅读数: 3
用户头像

TiAmo

关注

有能力爱自己,有余力爱别人! 2022-06-16 加入

CSDN全栈领域优质创作者,万粉博主;阿里云专家博主、星级博主、技术博主、阿里云问答官,阿里云MVP;华为云享专家;华为Iot专家;亚马逊人工智能自动驾驶(大众组)吉尼斯世界纪录获得者

评论

发布
暂无评论
精通Vue.js系列实例教程 │ Vue组件的数据监听_Vue_TiAmo_InfoQ写作社区