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

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 函数来创建一个响应式的计算属性,当依赖的响应式数据发生变化时,计算属性会重新计算并返回新的值。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QT 最近使用的项目配置文件

目录 1 QT 最近使用的项目配置文件所在路径 2 QtCreator.ini 1 QT 最近使用的项目配置文件所在路径 C:\Users\your username\AppData\Roaming\QtProject QtCreator.ini最好先备份一份 2 QtCreator.ini ProjectExplorer 下面的 RecentProjects\FileNames RecentProjects\…

希尔排序

文章目录 前言一.直接插入排序(时间复杂度N^2)二.希尔排序时间复杂度 前言 今天我们来讲一下排序算法中的插入排序中的希尔排序,插入排序分为两种,一种是直接插入排序,另一种就是希尔排序 一.直接插入排序(时间复杂度N^2) 我们这个排序,我们…

Day26 手撕各种集合底层源码(一)

Day26 手撕各种集合底层源码(一) 一、手撕ArrayList底层源码 1、概念: ArrayList的底层实现是基于数组的动态扩容结构。 2、思路: 1.研究继承关系 2.研究属性 3.理解创建集合的过程 – 构造方法的底层原理 4.研究添加元素的过程…

微机原理-基于8086倒计时多路抢答器系统

**单片机设计介绍,微机原理-基于8086倒计时多路抢答器系统 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 微机原理-基于8086倒计时多路抢答器系统概要主要关注于利用8086微处理器设计和实现一个具有倒计时功能的多路抢答器系统…

总结UDP协议各类知识点

前言 本篇博客博主将详细地介绍UDP有关知识点,坐好板凳发车啦~ 一.UDP特点 1.无连接 UDP传输的过程类似于发短信,知道对端的IP和端口号就直接进行传输,不需要建立连接; 2.不可靠传输 没有任何的安全机制,发送端发…

MySQL Innodb 引擎中预防 Update 操作上升为表锁

一、MySQL 如何预防 Update 上升为表锁 在 MySQL 中,进行任何数据的 修改 操作都会进行一定的锁操作,而锁的不同直接导致性能的差异。例如 MyISAM 引擎,更新时采用表锁,并发性较差。而 Innodb 引擎支持事务,更新时采用…

c++调用阿里云短信服务

💂 个人主页:pp不会算法^ v ^ 🤟 版权: 本文由【pp不会算法v】原创、在CSDN首发、需要转载请联系博主 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦 购买套餐包 申请资质 申请模板 申请签名 上面这些审核通过之后 添…

低代码平台与自动化软件开发的关系

引言 随着信息技术的不断发展,软件开发领域也在不断演进。在追求更高效、更快速的软件开发过程中,低代码平台和自动化软件开发技术日益受到关注。低代码平台以其可视化开发界面和快速构建应用的能力,为非专业开发人员提供了参与软件开发的机会…

预处理详解(一) -- 预定义符号与#define定义

目录 一. 预定义符号二. #define1.#define定义常量2.#define定义宏3.带有副作用的宏参数4.宏替换的规则5.宏和函数的对比 一. 预定义符号 %s _ _FILE_ _ //文件 %s _ _ DATE_ _ //日期 %s _ _ TIME_ _ //时间 %d _ _ LINE_ _ //行号 %d _ _ STDC_ _ //如果编译器支持 ANSI C,那…

【Vue】动态样式

内联样式的动态样式 body(){ boxASelect:false, } v-bind:style"{borderColor:boxASelect ? red : #ccc}" <body><header><h1>Vue Dynamic Styling</h1></header><section id"styling"><div class"demo&quo…

kubernetes(K8S)学习(七):K8S之系统核心组件

K8S之系统核心组件 K8s系统核心组件1.1 Master和Node1.2 kubeadm1.3 先把核心组件总体过一遍1.4 Kubernetes源码查看方式1.5 kubectl1.6 API Server1.7 集群安全机制之API Server1.8 Scheduler1.9 kubelet1.10 kube-proxy K8s系统核心组件 1.1 Master和Node 官网 &#xff1a;…

蓝桥杯刷题-重新排序

重新排序 差分&#xff1a; s,d [0]*100010,[0]*100010 tmp 0 n int(input()) a list(map(int,input().split())) a.insert(0,0) for i in range(1,n1):s[i] s[i-1] a[i] m int(input()) for _ in range(m):l,r map(int,input().split())# [l,r]的和tmp s[r] - s[l-1…

【AI】命令行调用大模型

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 【AI】命令行调用大模型引入正文初始化项目撰写脚本全局安装 成果展示 【AI】命令…

基于Spring Boot的在线学习系统的设计与实现

基于Spring Boot的在线学习系统的设计与实现 摘 要 在线学习系统是以大学传统线下教学方式不适应信息技术的迅速发展为背景&#xff0c;提高学习效率&#xff0c;解决传统教学问题&#xff0c;并且高效的实现教学信息化的一款软件系统。为了更好的实现对于教学和学生的管理&a…

【C++进阶】多态,带你领悟虚函数和虚函数表

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;c大冒险 总有光环在陨落&#xff0c;总有新星在闪烁 【本节目标】 1. 多态的概…

实验一 Python集成开发环境的搭建及可视化库的安装

一、安装集成开发环境 下载安装包 官方网址&#xff1a; Free Download | Anaconda 或者镜像网站下载&#xff08;较快&#xff09; https://repo.anaconda.com/archive/ 安装 配置环境变量 验证 输入&#xff1a; conda -V 二、下载pyecharts环境 点击 Anaconda Promp…

C++树状数组 (原理 + 代码 + lowbit解释)

目录: 什么是树状数组&#xff1f; 代码模板 原理 lowbit解释 什么是树状数组&#xff1f; 树状数组作为一种高效的数据结构&#xff0c;可以在O(logn)内完成更新和查询操作&#xff0c;因此非常适合加减&#xff0c; 区间和&#xff0c; 查询。 适合问题&#xff1a;…

【YOLOv8 代码解读】数据增强代码梳理

1. LetterBox增强 当输入图片的尺寸和模型实际接收的尺寸可能不一致时&#xff0c;通常需要使用LetterBox增强技术。具体步骤是先将图片按比例缩放&#xff0c;将较长的边缩放到设定的尺寸以后&#xff0c;再将较短的边进行填充&#xff0c;最终短边的长度为stride的倍数即可。…

Web APIs知识点讲解(阶段五)

DOM- 网页特效篇 一.课前回顾(手风琴) <!DOCTYPE html> <html><head lang"en"><meta charset"UTF-8"><title>手风琴</title><style>ul {list-style: none;}* {margin: 0;padding: 0;}div {width: 1200px;heig…

Mysql从0到1 —— CRUD/索引/事务

文章目录 1 预备知识1.1 安装1.2 登录 & 退出1.3 配置文件my.cnf 2 基础知识2.1 链接服务器2.2 什么是数据库2.3 基本使用2.3.1创建表2.3.2 插入数据 2.4 服务器、数据库、表的关系2.5 SQL分类2.6 存储引擎 3 Mysql数据库的操作3.1 创建和删除3.2 字符集和校验规则3.3 查看…