vue源码解析—— watch/computed的实现逻辑和区别

news2024/11/27 14:48:35

watchcomputed 是 Vue 中的两个重要的响应式属性,它们在实现机制和使用上存在一些区别。

  • watch:用于监听数据的变化,并在数据变化时执行回调函数。可以使用 deep 配置项来开启深度监听,监听数据的子属性变化。可以使用 immediate 配置项来立即执行回调函数。
  • computed:用于计算数据,并在数据变化时重新计算数据。可以使用 get 函数来计算数据,并在依赖的数据变化时重新计算数据。可以使用 set 函数来设置数据,并在数据变化时执行回调函数。

  Vue 2 中,使用 Object.defineProperty 来实现数据响应式,并且在数据变化时通过 getter 函数来依赖收集和发布订阅。在 Vue 3 中,使用 Proxy 来实现数据响应式,并且在数据变化时通过 get 函数来依赖收集和发布订阅。这也导致了在源码实现上vue2和vue3的不同。

watch 和 computed

watch用法

watch 用于监听数据的变化,当数据变化时,可以执行相应的操作。它可以用在数据的深层次监听异步操作、或者需要在数据变化后执行某个操作的场景。

new Vue({
  data: {
    message: 'hello'
  },
  watch: {
    message(newVal, oldVal) {
      console.log('message changed:', newVal, oldVal)
    }
  }
})

在上面的例子中,我们监听了 message 数据的变化,当 message 数据变化时,就会执行 console.log 函数。 

watch 还有一些高级用法,比如监听对象的变化,可以使用 deep 选项,如下: 

new Vue({
  data: {
    user: {
      name: 'John',
      age: 27
    }
  },
  watch: {
    user: {
      handler(newVal, oldVal) {
        console.log('user changed:', newVal, oldVal)
      },
      deep: true
    }
  }
})

computed用法

computed 用于计算数据,它可以基于数据的变化,动态地计算出新的数据。computed 的值会被缓存,只有当它依赖的数据发生变化时,才会重新计算

computed 的基本用法如下:

new Vue({
  data: {
    message: 'hello',
    reversedMessage: ''
  },
  computed: {
    reversedMessage() {
      return this.message.split('').reverse().join('')
    }
  }
})

在上面的例子中,我们定义了一个 reversedMessage 计算属性,它会动态地计算出 message 的反转字符串。当 message 变化时,reversedMessage 也会随之变化。

computed 还有一些高级用法,比如可以使用 getset 函数来实现双向数据绑定,如下:

new Vue({
  data: {
    message: 'hello'
  },
  computed: {
    fullName: {
      get() {
        return this.firstName + ' ' + this.lastName
      },
      set(value) {
        const names = value.split(' ')
        this.firstName = names[0]
        this.lastName = names[names.length - 1]
      }
    }
  }
})

watch和computed的区别、使用场景

watchcomputed 都可以用来监听数据的变化,但它们的用途和实现方式有所不同。

  • watch 是一个监听器,它可以监听数据的变化,并执行相应的操作。watch 可以用在数据的深层次监听异步操作、或者需要在数据变化后执行某个操作的场景。
  • computed 是一个计算属性,它可以基于数据的变化,动态地计算出新的数据。computed 的值会被缓存,只有当它依赖的数据发生变化时,才会重新计算。computed 可以用在需要对数据进行计算的场景,比如需要对数据进行过滤、排序、或者格式化的场景。computed不支持异步逻辑。

总的来说,watch 更适用于需要执行某个操作的场景,而 computed 更适用于需要对数据进行计算的场景。

面试官问:computed里可以进行异步操作吗?

在Vue中,computed属性默认是同步的,不支持直接进行异步操作。如果需要在computed中进行异步操作,可以使用async/await结合一个异步函数来实现。但是需要注意的是,computed属性应该是一个同步的计算属性,而不应该依赖于异步操作的结果。异步操作应该放在methods中进行处理,或者使用watch监听数据的变化。这样可以确保computed属性的计算是同步的,避免出现意外的行为。

 watch源码解析

vue2

Vue 2 可以实现在监视属性的值发生变化时,触发对应的回调函数,并在回调函数中执行相应的操作。主要通过这两个方法 initWatch 和createWatcher实现。

initWatch函数遍历 watch 对象的所有属性,如果 handler 是一个数组,则遍历数组中的每个元素,并为每个元素创建一个监视器,如果 handler 是一个函数或一个纯对象,则直接创建一个监视器。createWatcher 函数用于创建一个监视器,createWatcher 函数最终返回 vm.$watch 函数的执行结果,即一个监视器对象。

 $watch方法本质上创建一个watcher,监听数据变化,数据变化后触发回调

 

vue3

watch 函数做了哪些事情:

1.watch 函数会在内部创建一个 Watcher 对象,并将其添加到当前组件的 watcher 队列中。当响应式数据变化时,会触发 Watcher 对象的更新函数,执行回调函数 cb。 

watch 函数接收三个参数:

source可以是一个响应式数据对象,也可以是一个 getter 函数,用于获取需要监听的数据。

cb回调函数,在数据变化时执行,可以接收三个参数:newValoldValonInvalidate

options可选参数,用于配置监听器函数的执行时机和调度方式,包括:

  • deep:是否开启深度监听。
  • immediate:是否立即执行回调函数。
  • flush:控制回调函数的调度执行时机,可以选择在同步或异步更新后执行。
  • onTrack:在监听器函数执行前调用。
  • onTrigger:在监听器函数执行后调用。

Watcher 对象的doWatch函数会执行以下步骤:

Watcher 对象的 doWatch 函数中,会创建一个 effect 函数,并将其与 scheduler 函数关联起来。当响应式数据变化时,会触发 effect 函数,执行 scheduler 函数,从而执行 watch 函数中定义的回调函数 cb

如果 getter 函数执行完成后,需要取消监听,可以调用 watch 函数返回的 StopWatch 对象的 stop 方法,从当前组件的 watcher 队列中移除 Watcher 对象。

可以看下job或SchedulerJob做了什么

执行 scheduler 函数,从而执行 watch 函数中定义的回调函数 cb,并在需要的情况下,更新新值和旧值的值,并执行 onInvalidate 函数。

在watch方法里提供了onCleanup方法,可以清除上一次的异步操作结果 

computed源码解析

computed的实现原理在面试中经常被问到,如果你只知道维护了一个dirty属性是远远不够的。computed为什么可以是一个属性,依赖的项的变化后,怎么通知computed属性的更改,甚至触发模板的渲染呢?这些都要去源码中获得答案。

vue2

vue2中源码地址:vue-main\src\core\instance\state.ts

 vue2源码整体思想如下:

  1. 计算属性会创建一个计算属性watcher,这个watcher{lazy:true}不会立刻执行
  2. 通过Object.defineProperty将计算属性定义到实例上
  3. 当用户取值时会触发getter,拿到计算属性对应的watcher,看dirty是否为true,如果为true则求值。
  4. 让计算属性watcher中依赖的属性收集最外层的渲染watcher,可以做到依赖的属性变化了,触发计算属性更新。
  5. 如果依赖的属性没有变化,采用缓存

 1.计算属性会创建一个计算属性watcher,这个watcher{lazy:true}不会立刻执行

watcher 的主要作用是:

  • 依赖收集:在 watcher 创建时,会将依赖的数据添加到 watcherdep 属性中,当依赖的数据变化时,会通知 watcher 执行回调函数。
  • 发布订阅:在 watcher 创建时,会将 watcher 添加到 depsubs 属性中,当依赖的数据变化时,会通知 dep 执行 notify 函数,从而执行 watcher 的回调函数。

 2.通过Object.defineProperty将方法定义为属性并绑定到实例上,这也是为什么计算属性是个方法却能得到属性

3.computed的核心逻辑:当用户取值时会触发getter,拿到计算属性对应的watcher,看dirty是否为true,如果为true则求值。

并且让计算属性watcher中依赖的属性收集最外层的渲染watcher,可以做到依赖的属性变化了,触发计算属性更新。如果依赖的属性没有变化,采用缓存

vue3

vue3的核心逻辑也是维护一个dirty属性;只不过增加了dirty级别,对dirty进行了分类

与vue2不同的是,vue3依赖收集不依赖watcher,而是改成了effect。

Vue 3 中的 computed 是通过 effect 函数和 computed 函数实现的。当定义一个 computed 计算属性时,Vue 3 会使用 effect 函数来创建一个响应式的计算属性对象,并在计算属性的 getter 函数中访问其他响应式数据。这样,计算属性就建立了与依赖数据的关联,当依赖数据变化时,计算属性会重新计算并返回新的值。

  1. effect 函数effect 函数是 Vue 3 中用于创建响应式副作用的函数。它接收一个函数作为参数,并在函数内部访问响应式数据时建立数据与副作用函数之间的依赖关系。当响应式数据发生变化时,effect 函数会重新运行副作用函数,从而触发更新。

  2. computed 函数computed 函数用于创建计算属性。在 Vue 3 中,computed 函数接收一个 getter 函数作为参数,该 getter 函数内部访问其他响应式数据,并返回一个计算值。computed 函数会使用 effect 函数来创建一个响应式的计算属性,当依赖的响应式数据发生变化时,计算属性会重新计算并返回新的值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1556630.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

安装 kubesphere 插件报错Error: UPGRADE FAILED: \“ks-minio\“ has no deployed releases

安装 kubesphere 插件报错Error: UPGRADE FAILED: “ks-minio” has no deployed releases TASK [common : Kubesphere | Creating manifests] ******************************** ok: [localhost] > (item{uname: ucustom-values-minio, ufile: ucustom-values-minio.yaml}…

Linux shell编程学习笔记43:cut命令

0 前言 在 Linux shell编程学习笔记42:md5sum 中,md5sum命令计算md5校验值后返回信息的格式是: md5校验值 文件名 包括两项内容,前一项是md5校验值 ,后一项是文件名。 如果我们只想要前面的md5 校验值&#xff0c…

完整部署一套k8s-v.1.28.0版本的集群

一、系统情况 虚拟机版本:esxi 6.7 系统版本:centos7.9_2009_x86 配置:4核8G(官网最低要求2核2G) 192.168.0.137 master节点 192.168.0.139 node2节点 192.168.0.138 node1节点(节点扩容练习&#xf…

拥抱挑战,开启增长:2024年全球产品团队的OKR策略

2024年,全球经济格局进入重塑阶段。消费者在消费选择上趋于严苛,企业需推出更具吸引力的产品与服务,以赢得消费者的青睐。同时,企业需通过持续创新,提升产品竞争力,方能在充满挑战的市场环境中实现持续增长…

Kaggle注册验证码问题(Captcha must be filled out.)

Kaggle注册验证码问题 Captcha must be filled out.使用Edge浏览器 Header Editor 插件安装 下载插件Header Editor 导入重定向脚本 点击扩展插件, 打开Header Editor插件,进行管理 点击导入输入下载链接进行下载或者导入本地json文件(二者任选其一…

文件操作(顺序读写篇)

1. 顺序读写函数一览 函数名功能适用于fgetc字符输入函数所有输入流fputc字符输出函数所有输出流fgets文本行输入函数所有输入流fputs文本行输出函数所有输出流fscanf格式化输入函数所有输入流fprintf格式化输出函数所有输出流fread二进制输入文件fwrite二进制输出文件 上面说…

代码学习记录31---动态规划开始

随想录日记part31 t i m e : time: time: 2024.03.29 主要内容:今天开始要学习动态规划的相关知识了,今天的内容主要涉及四个方面: 理论基础 ; 斐波那契数 ;爬楼梯 ;使用最小花费爬楼梯。 理论基础 509. 斐…

代码随想录算法训练营第二十四天(回溯1)|77. 组合(JAVA)

文章目录 回溯理论基础概念类型回溯模板 77. 组合解题思路源码 回溯理论基础 概念 回溯是递归的副产品,本质上是一种穷举 回溯解决的问题可以抽象为一种树形结构 类型 回溯主要用来解决以下问题 组合问题:N个数里面按一定规则找出k个数的集合切割问…

自己动手用ESP32手搓一个智能机器人:ESP32-CAM AI Robot

目录 介绍 硬件需求 软件需求 步骤 总结 源码下载 介绍 ESP32-CAM是一款集成了Wi-Fi和蓝牙功能的微控制器模块,同时还集成了摄像头接口,使其成为一个非常适合构建智能机器人的选择。在本项目中,我将向您展示如何使用ESP32-CAM模块构建…

NSSCTF Round#20 Basic 真亦假,假亦真 CSDN_To_PDF V1.2 出题笔记 (附wp+源码)

真亦假&#xff0c;假亦真 简介&#xff1a;java伪造php一句话马。实则信息泄露一扫就出&#xff0c;flag在/flag里面。 题目描述&#xff1a;开开心心签个到吧&#xff0c;祝各位师傅们好运~ 静态flag&#xff1a;NSS{Checkin_h4v3_4_g00D_tINNe!} /路由显示 <?php e…

沙箱安全机制

Java安全模型的核心就是Java沙箱(sandbox)&#xff0c; 什么是沙箱&#xff1f; 沙箱是一个 限制程序运行的环境。沙箱机制就是将Java代码限定在虚拟机(JVM) 特定的运行范围中&#xff0c;并且严格限制代码对本地系统资源访问&#xff0c;通过这样的措施来保证 对代码的 有效隔…

FANUC机器人故障诊断—报警代码更新(三)

FANUC机器人故障诊断中&#xff0c;有些报警代码&#xff0c;继续更新如下。 一、报警代码&#xff08;SRVO-348&#xff09; SRVO-348DCS MCC关闭报警a&#xff0c;b [原因]向电磁接触器发出了关闭指令&#xff0c;而电磁接触器尚未关闭。 [对策] 1.当急停单元上连接了CRMA…

密码CTF

二、[SWPUCTF 2021 新生赛]crypto6----base 1.题目 var"************************************" flagNSSCTF{ base64.b16encode(base64.b32encode(base64.b64encode(var.encode()))) } print(flag) 小明不小心泄露了源码&#xff0c;输出结果为&#xff1a;4A5A4…

linux离线安装jdk

一、下载jdk 地址: Java Downloads | Oracle 中国 具体下载什么版本要根据安装的linux系统架构来决定&#xff0c;是ARM64还是X64&#xff0c;linux命令行输入如下命令 uname -m 可以看到linux系统是x64 架构(x86是32位&#xff0c;x86_64是64位&#xff0c;由于x86已经淘汰&…

设计模式之代理模式精讲

代理模式&#xff08;Proxy Pattern&#xff09;也叫委托模式&#xff0c;是一个使用率非常高的模式&#xff0c;比如我们在Spring中经常使用的AOP&#xff08;面向切面编程&#xff09;。 概念&#xff1a;为其他对象提供一种代理以控制对这个对象的访问。 代理类和实际的主题…

蓝桥杯【奇怪的捐赠】c语言

我会将这题的解题的核心思路解为将10进制转化成7进制&#xff0c;毕竟题目上说的很清楚7的几次方 然后附上我认为的最优解 #include<stdio.h> int main() {int n 1000000;int sum 0;while (n ! 0){int a;a n % 7;n n / 7;sum a ;}printf("%d", sum);retu…

C++中STL中容器--string讲解

C中STL中容器--string讲解 一、标准库中的string类1.1 string类说明 二、string类的常用接口2.1 string类对象的常见构造2.2 string类对象的容量操作2.3 string类对象的访问及遍历操作2.4 string类对象的修改操作2.5 string类非成员函数 三、string的结构3.1 VS下string的结构3…

计算机视觉的应用27-关于VoVNetV2模型的应用场景,VoVNetV2模型结构介绍

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用27-关于VoVNetV2模型的应用场景&#xff0c;VoVNetV2模型结构介绍。VoVNetV2&#xff08;Visual Object-Driven Representation Learning Network Version 2&#xff09;是一种深度学习模型&#x…

vue-ueditor-wrap上传图片报错:后端配置项没有正常加载,上传插件不能正常使用

如图所示&#xff0c;今天接收一个项目其中富文本编辑器报错 此项目为vue2项目&#xff0c;富文本编辑器为直接下载好的资源存放在public目录下的 经过排查发现报错的函数在ueditor.all.min.js文件内&#xff0c;但是ueditor.all.min.js文件夹是经过压缩的 所以直接&#xff…

stream流中的坑,peek/map/filter

起因 所在系统为一个对账系统&#xff0c;涉及的业务为发布账单&#xff0c;数据结构定的是供应商账单发布&#xff0c;生成企业账单和个人账单。发布账单处理完本系统业务后&#xff0c;需要生成站内通知和调用外部接口生成短信通知。后来增加需求&#xff0c;需要在发布完成…