Vue3源码梳理:响应式系统的前世今生

Vue3-响应式原理解析 vue3 与 vue2 主要差异之一无疑是响应式实现上的改变。本文主要阐述响应式原理的实现方式解析以及核心源码阅读的注释理解。本文主要对响应式实现原理进行逻辑梳理,舍弃枯燥无味的代码,只用图解/文字进行功能描述,具体实现请自行阅读。保重!!!如果问题,虚心求教,还请指出!!! 阅读详情

响应性数据的前世


  • js的程序性: 一套固定的,不会发生变化的执行流程

1 )没有响应的数据

// 定义商品对象
const product = {
  price: 10,
  quantity: 2
}

// 总价格
let total = product.price * product.quantity
console.log(`总价格:${total}`) // 20

// 修改商品的数量
product.quantity = 5
console.log(`总价格:${total}`) // 20
  • 这是一段非常普通的js程序,当最后 product.quantity 发生改变的时候,最终结果并没有发生变化
  • 这里,当商品数量发生变化,总价格也会发生变化是我们的期望
  • 由于js程序性的约束,我们只能得到20,我们想让程序变得更智能

2 )进一步改造

// 定义商品对象
const product = {
  price: 10,
  quantity: 2
}

// 总价格
let total = 0

// 定义一个 effect 函数
const effect = () => {
  total = product.price * product.quantity // 访问属性,这里是 getter行为
}

effect()
console.log(`总价格:${total}`) // 20

// 修改商品的数量
product.quantity = 5 // 修改属性,这里是 setter 行为

effect() // 注意这里
console.log(`总价格:${total}`) // 50
  • 这里,封装了一个effect方法,这个方法是重新计算 total 的方法
  • 当 product.quantity 数据发生改变的时候,手动调用了一次 effect 方法
  • 以上的方式是每次手动触发 effect 方法进行一次 类似 getter 操作
  • 这样手动操作,是比较麻烦的
  • 为此,js中的API可以有效解决这个问题

响应式数据的今生


1 )关于响应性数据

  • 响应数据:是指影响视图变化的数据

2 ) vue2核心响应式API Object.defineProperty() 方法

let quantity = 2
const product = {
  price: 10,
  quantity
}

// 总价格
let total = 0

// 计算总价格函数
const effect = () => {
  total = product.price * product.quantity
}

effect()
console.log(`总价格:${total}`) // 20

// 响应式变化
Object.defineProperty(product, 'quantity', {
  set(newVal) {
    console.log('setter')
    quantity = newVal
    effect()
  },
  get() {
    console.log('getter')
    return quantity // 这里的变量是暴露在最外面的,不是很好
  }
})
  • 这样可以在指定对象上,指定属性上的 getter 和 setter 行为,以此来触发effect(更新程序)
  • 这样来说,相对更智能了

3 ) Obeject.defineProperty() 在设计上的缺陷

  • 存在一个致命缺陷:vue官网/深入响应式原理/检测变化的注意事项
    • 由于js的限制,vue不能检测数组和对象变化

代码示例,如下

<template>
  <div>
    <ul>
      <li v-for="(val, key, index) in obj" :key="index">
        {{ key }} --- {{ val }}
      </li>
    </ul>
    <button @click="addObjKey">为对象增加属性</button>
    <div> ---------------- </div>
    <ul>
      <li v-for="(item, index) in arr" :key="index">
        {{ item }} --- {{ index }}
      </li>
    </ul>
    <button @click="addArrItem">为数组增加元素</button>
  </div>
</template>
<script>
  export default {
    name: 'App',
    data() {
      return {
        obj: {
          name: '张三',
          age: 30
        },
        arr: [
          '张三', '李四'
        ]
      }
    },
    methods: {
      addObjKey() {
        this.obj.gender = '男'
        console.log(this.obj)
      },
      addArrItem() {
        this.arr[2] = '王五'
        console.log(this.arr)
      }
    }
  }
</script>
  • 上面两个按钮点击后,数据会更新,但是页面视图不会更新
  • 当对象新增一个没有在data中声明的属性时,新增的属性不是响应式的
  • 当为数组通过下标形式新增一个元素时,新增的元素不是响应式的
  • why?
    • Object.defineProperty 只能监听指定对象,指定属性的 getter 和 setter
    • js限制是指:没有办法知道为某一个对象新增了某一个属性这类行为,新增属性会失去响应性

4) Vue3中的 Proxy

  • 文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

  • Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

  • 语法

    const p = new Proxy(target, handler)
    
    • target 表示proxy包装的目标对象,可以是任何对象: 原生数组,函数,甚至另一个对象
    • p 是 proxy的实例,是代理对象
    • handler 是一个对象,可以在这个对象上指定getter和setter

代码改造,示例如下

// 定义商品对象
const product = {
  price: 10,
  quantity: 2
}

// 生成代理对象, 注意事项:使用时不能使用被代理对象(原对象),而应该使用代理对象
// proxy 代理的是整个对象,而非某个对象的某个属性
const proxyProduct = new Proxy(product, {
  set(target, key, newVal, receiver) {
    // console.log('setter')
    target[key] = newVal
    // 这里触发 effect 重新计算
    effect()
    return true
  },
  get(target, key, receiver) {
    // console.log('getter')
    return target[key]
  }
})

// 总价格
let total = 0

// 定义一个 effect 函数
const effect = () => {
  total = proxyProduct.price * proxyProduct.quantity // 访问属性,这里是 getter行为
}

effect()
console.log(`总价格:${total}`) // 20

// 修改商品的数量, 注意这里是修改的代理对象的值,而非被代理对象的值
proxyProduct.quantity = 5 // 修改属性,这里是 setter 行为

effect()
console.log(`总价格:${total}`) // 50
  • 通过修改代理对象的值,来让被代理对象同步发生变化
  • 这里使用 proxy 完成了 和 Object.defineProperty一样的效果
  • 总结:
    • proxy:
      • Proxy 将一个对象 (被代理对象), 得到一个新的对象 (代理对象), 同时拥有被代理对象中所有的属性
      • 当想要修改对象的指定属性时,我们使用 代理对象 进行修改
      • 代理对象的任何一个属性都可以触发 handler 的getter和setter
    • Object.defineProperty
      • 该API为指定对象的指定属性 设置 属性描述符
      • 当想要修改对象的指定属性时,可以使用原对象进行修改
      • 通过属性描述符,只有 被监听 的指定属性,才可以触发 getter 和 setter
    • 所以,当 vue3 通过 Proxy 实现响应性核心 API 之后, vue 将不会再存在新增属性时失去响应性的问题

5 ) proxy的最佳合伙API: Reflect, 拦截js对象操作

  • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect

  • Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与 proxy handler (en-US) 的方法相同。Reflect 不是一个函数对象,因此它是不可构造的。

    const obj = { name: '张三' }
    Reflect.get(obj, 'name') // '张三'
    
  • Reflect.get(target, propertyKey[, receiver])

    • 可以看到,这个 API 有三个参数
    • target 需要取值的目标对象
    • propertyKey 需要获取的值的键值
    • receiver 如果target对象中指定了getter,receiver则为getter调用时的this值

测试代码如下:

// p1 对象
const p1 = {
  lastName: '张',
  firstName: '三',
  get fullName() {
     return this.lastName + this.firstName
  }
}

// p2 对象
const p2 = {
  lastName: '李',
  firstName: '四',
  get fullName() {
     return this.lastName + this.firstName
  }
}

// 测试
console.log(p1.fullName) // 张三
console.log(Reflect.get(p1, 'fullName')) // 张三
console.log(Reflect.get(p1, 'fullName', p2)) // 李四 这里,改变了getter的this指向,this指向了 p2, 所以 getter中获取的是 p2的fullName属性

console.log(p2.fullName) // 李四

使用 proxy 和 Reflect 一起使用

// p1 对象
const p1 = {
  lastName: '张',
  firstName: '三',
  get fullName() {
     return this.lastName + this.firstName
  }
}

const proxy = new Proxy(p1, {
  get(target, key, receiver) {
    console.log('getter')
    return target[key]
  }
})

console.log(proxy.fullName) // 这里进行一次getter操作,会执行一次
  • 上述代码只会触发一次getter, 因为其中的this指向是target,也就是原对象p1
  • 但是,我们的理解,如果做任意的取值都会触发一次getter, 也就是 访问fullName的时候会触发一次getter, 但是fullName里面也有两次getter
  • 这时候,我们想要触发三次getter 如何修改呢
// p1 对象
const p1 = {
  lastName: '张',
  firstName: '三',
  get fullName() {
     return this.lastName + this.firstName
  }
}

const proxy = new Proxy(p1, {
  get(target, key, receiver) {
    console.log('getter', key)
    // return target[key]
    return Reflect.get(target, key, receiver) // 注意,修改这里
  }
})

console.log(proxy.fullName) // 这里进行一次getter操作,会执行一次
  • 这时候 proxy.fullName 会触发 三次 getter的行为
    • 先输出:fullName 的getter
    • 再输出:lastName 的getter
    • 最后输出:firstName 的getter
  • 某些场景下,使用 return target[key] 会存在bug,
  • 请使用 return Reflect.get(target, key, receiver) 代替
UG/NX二次开发实战:用Python脚本批量更新100个零件参数,解放你的双手 本文详细介绍了如何利用NXOpen Python API进行UG/NX二次开发,实现批量更新100个零件参数的自动化流程。通过环境配置、框架设计、核心代码实现及性能优化,帮助工程师高效完成参数化设计任务,显著提升工作效率。 阅读详情

相关推荐

FPS游戏自动枪械识别+压枪(以PUBG为例)

使用Python3.9作为开发环境,实现一个通用的FPS游戏自动检测枪械配件+压枪宏的软件开发思路

weixin_37827742的博客 3万+

从Vue3源码深入理解ref响应式

首先我们要去学习源码,就要对整个项目有一个基本了解,了解每个模块、每个目录的作用,这样对我们后期阅读源码也会有很大的帮助;我们将项目从Github上克隆下来:bash代码解读复制代码如果因为网络问题克隆不下来,点击这个链接获取源码;笔者克隆下来时,当前版本号为3.5.12。我们看到整个项目的目录还是非常多的,又有编译时,又有运行时的目录,主要的代码都存放在packages目录,因此我们主要看下它下面的目录结构:bash代码解读复制代码。

qq_66118130的博客 1020

共享单车需求预测问题:建模篇

机器学习训练营——机器学习爱好者的自由交流空间(qq群号:696721295) 在上一篇里,我们使用可视化的方法分析了“共享单车”的数据集,理解了特征(变量)之间的关系。我们将在本篇建立几个模型预测单车的需求量,并比较它们的预测效果。 随机森林填充法 首先,导入必需的库、训练集和检验集。 import pylab import calendar import numpy as np impor...

wong2016的博客 1万+

Vue3源码梳理:源码目录结构及源码阅读方法

源码目录结构及源码阅读方法

Wang的专栏 2078

WPS Office 2026免费安装指南:AI办公与安全使用全解析

办公软件作为数字化办公的核心工具,其基本原理是通过文档处理、数据计算和演示展示等功能模块满足用户日常办公需求。随着人工智能技术的发展,现代办公软件逐步集成AI能力,通过自然语言处理和数据分析算法提升工作效率。在技术价值层面,优秀的办公软件不仅需要保持格式兼容性和跨平台同步能力,更要注重用户数据安全防护。从应用场景来看,无论是个人文档编辑、企业报表分析还是团队协作演示,办公软件都发挥着关键作用。WPS Office 2026作为国产办公软件代表,深度融合AI助手功能,支持智能写作和数据分析,同时通过官方免费策

weixin_30599769的博客 737

基于YOLOv12的牙齿识别与计数系统:从理论到完整实现

本文详细介绍了一种基于YOLOv12深度学习模型的牙齿识别与计数系统。该系统能够在口腔X光片中准确识别和计数牙齿,并提供了用户友好的UI界面。文章涵盖了数据预处理、模型训练、性能优化以及完整系统实现的各个环节,并提供了可复现的代码和数据集参考。整个系统包含三个主要模块:数据预处理模块YOLOv12模型训练模块UI界面展示模块。

YOLO 179

【vue3】04. 跟着官网学习vue3-响应式基础

跟着官网学习vue3 响应式原理

有一棵树的博客 1905

【VxWorks】Vxworks、QNX、Xenomai、Intime、Sylixos、Ucos等实时操作系统的性能特点

VxWorks 操作系统是美国WindRiver公司于1983年设计开发的一种嵌入式实时操作系统,是嵌入式开发环境的关键组成部分。其内核支持多任务调度(采用基于优先级抢占方式,同时支持同优先级任务间的分时间片调度)、任务间的同步、进程间通信机制、中断处理、定时器和内存管理机制等,且提供了一个快速灵活的与 ANSIC 兼容的 I/O 系统,网络支持 对其它VxWorks系统和TCP/IP网络系统的"透明"访问。具有良好的持续发展能力、良好的可靠性、卓越的实时性和高性能的内核以及友好的用户开发环境。

smartvxworks的博客 1065

从原理和源码理解Vue3的响应式机制

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。

发果叁 541

TensorFlow2深度学习实战(十四):目标检测算法 YOLOv4 解析

最易上手的【计算机视觉】小白教程!

不积跬步,无以至千里! 7636

文章ACCEPT之后填写内容教程-Springer-Verlag即施普林格

1. 确认corresponding author通讯作者机构是否正确(最好细分到系,如搜不到系只填大学也可)这些信息因为在投稿的时候都填过,所以一般都没问题,可确认并继续。2. Co-Author: 这篇文章有没有其他作者,如果有就选yes,并确认投稿时所填的作者姓名和机构是否正确。这个也是如果前期都填对了就不用修改。3. Research funders: 问文章的研究有没有资助,没有就选no,有就选yes,并填写机构名称和grant/contact numbers。

ZhangJiqun&191cm&85kg 4796

Vue3源码解读--方向篇

vue源码解读

嗨,欢迎来到我的 优快云 博客小天地!一名深耕多年的技术发烧友。在这里,我将把日常工作中积累的宝贵经验,从复杂架构设计的精妙之处,到代码优化的实战技巧,毫无保留地分享给大家。 1679

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Vue 响应式数据失效全解析:从原理机制到工程实践

在 Vue 开发中,“修改了数据但界面未更新” 是最令开发者头疼的问题之一。这通常源于对响应式系统边界的误解。本文将从底层源码逻辑与工程实践两个维度,结合 Vue 2 与 Vue 3 的核心差异,提供系统性的解决方案。问题场景Vue 2 解决方案Vue 3 解决方案底层根源新增对象属性直接赋值Vue 2 劫持不到新 key;Vue 3 Proxy 拦截全量操作数组索引修改或splice直接赋值Vue 2 不监听数组索引;Vue 3 Proxy 监听解构响应式对象避免解构,或使用computed包装。

csdn_silent的博客 2233

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

3.为什么我的vue数据修改后视图未更新,梳理Vue2和Vue3中常见的数据不更新场景

Vue数据不更新问题的核心在于响应式机制的实现。Vue2受限于Object.defineProperty,需通过辅助方法(如$set)弥补不足;而Vue3通过Proxy提供了更完善的响应式支持。无论是Vue2还是Vue3,理解其原理并遵循“数据驱动视图”的原则,配合最佳实践,都能有效避免视图更新异常。

qq_1401820561的博客 2683

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Vue3源码梳理:关于运行时和编译时

上述 v-if 和 m-com 自定义组件不是正常的html, 浏览器是不认识的,所以不会解析。.vue文件是单文件组件, 在template中写入的html是真实的html标记节点么?vue中的编译时,准确来说是编译器的意思,核心代码在 compiler-core模块下。运行时做不到的事情,通过html标签结构的方式来渲染解析成真实的dom节点。初次渲染,会生成一个ul标签,同时生成三个li标签,并把他们挂载到div中。这个过程主要是:让假标记,被渲染成真实的html标签,最终呈现给用户。

Wang的专栏 990

Vue3源码梳理:watch监听函数的核心实现

watch 可以监听响应式数据的变化,从而触发指定的函数watch 函数监听三个参数监听的响应式对象回调函数 cb配置对象 optionsimediate: 初始化完成后会被立刻触发一次deep: 深度监听测试示例: watch.html,测试vue源码的watch函数name : '张三' }) watch(obj ,(val , oldVal) => {clearTimeout(timer) obj . name = '李四' } , 2000)

Wang的专栏 2154

vue+bootstrap响应式布局_实现 Vue 的响应式系统

前言Vue 最独特的特性之一,是其非侵入性的响应式系统。比如我们修改了数据,那么依赖这些数据的视图都会进行更新,大大提高了我们的"搬砖"效率,回想一下初学 JS 的时候海量的 Dom操作~.~......,Vue 通过数据驱动视图,极大的将我们从繁琐的DOM操作中解放出来。如下图,我们改变了 msg 的值,视图也响应式的进行了更新Vue 响应式原理我们先看 vue 官网的图,其实不太清晰,我初看的...

weixin_40005373的博客 1415

vue3响应式核心 reactivity 源码逻辑分析

当创建reactive后,会对传入对象进行proxy代理,在被订阅者使用(get)时,触发track函数 收集依赖,记录下“订阅者在这里用到当前reactive啦!”,在对象被修改(set)时,触发trigger函数 触发依赖,“要去更新所有用到当前reactive的订阅者!”......

block_xu的博客 523

Vue3源码 第一篇-总览

Vue3源码总览 文章目录Vue3源码总览前言一、从哪里搞到源码?二、开始学习1.准备所需文件,并且引入Vue32.如何读源码3.Vue.createApp()3.1 ensureRenderer()总结 前言 之前读过Vue2的源码,所以Vue3源码发布后就迫不及待的赶紧下载下来读了读,对比Vue2的源码还是有许多改变,但是对Vue2源码有所了解还是比较好读的,我自己也是一边读代码一边记笔记,把自己的一些笔记和想法分享在这里,如果有错误的地方希望大佬斧正,废话不多说,开冲~~ 提示:以下是本篇文章

不忘初心 1714

Vue2.x 源码 - 响应式原理

上一篇:Vue2.x 源码 - VNode渲染过程(update、patch) Vue2.x 实现响应式的核心是利用的 ES5 的 Object.defineProperty ,这也是 Vue 不兼容 IE8 及其以下浏览器的原因; 文章目录响应式对象 响应式对象 Object.defineProperty 的作用就是直接在一个对象上定义一个新属性,或者修改一个已经存在的属性,并返回该对象; Object.defineProperty(obj, prop, descriptor) obj :需要定义.

weixin_43299180的博客 1854

Vue 3 核心原理与实战:从响应式到企业级应用

Vue 3 核心原理与实战指南 本文全面解析 Vue 3 的核心技术升级与最佳实践。重点探讨了: 响应式系统革命:采用 Proxy 替代 defineProperty,实现更高效的属性监听和惰性劫持 编译时优化:通过 Patch Flag 和静态提升技术大幅提升虚拟 DOM 性能 开发范式演进:Composition API 提供更好的逻辑复用与 TypeScript 支持 现代化工具链:推荐使用 Vite 构建工具、Pinia 状态管理和 Nuxt 3 全栈框架 文章通过代码示例和原理对比,帮助开发者从

1221

Vue3 响应式原理深析:Proxy 源码拆解、ref/reactive 区别与依赖收集完整流程

Hi,我是前端人类学!Vue3 用 Proxy 重构响应式,弥补 Vue2 defineProperty 缺陷。本文从源码拆解响应式内核,区分 ref 与 reactive,梳理依赖收集到派发更新完整流程。

chaosweet的博客 503

Vue3源码梳理:computed计算属性的核心实现

计算属性 computed 会基于其响应式依赖被缓存,并且在依赖的响应式数据发生变化时,重新计算测试示例: computed.html,测试vue源码的属性name : '张三' }) const computedObj = computed(() => {return '姓名: ' + obj . value . name }) effect(() => {clearTimeout(timer) obj . value . name = '李四' } , 2000)

Wang的专栏 1304

Vue3源码梳理:ref的响应性核心实现

ref做的三件事情返回RefImpl实例在构造函数中,对传入数据进行处理:当前传入value是简单类型,则直接使用;否则,使用reactive函数包裹,让ref.value的值变成响应式数据启动两个函数:get value, set value。

Wang的专栏 761

探索Vue.js核心:手写Vue3源码(mini版)

探索Vue.js核心:手写Vue3源码(mini版) 项目介绍 你是否曾经好奇过Vue.js是如何工作的?你是否想要深入了解Vue3的内部机制?如果是,那么这个开源项目——Vue-源码实现(mini版),正是为你量身定制的。这个项目不仅提供了详细的代码注解,还配备了丰富的文章讲解,帮助你一步步理解Vue3的核心实现。 通过这个项目,你将能够清晰地看到Vue3的运行流程,从响应式系统的实现到Vue应...

gitblog_00544的博客 381

vue响应式源码解析(一)

vue响应式源码解析(一) vue.js逐渐热门,在前端框架领域崭露头角这几年,也逐渐成为了前端面试的必考知识点,下面我会通过一系列源码解析的文章来帮助大家梳理这一部分的内容,如有对我的观点持有异议,请在评论区留言,友好交流。 vue的响应式是怎么实现的? 响应式,即vue的数据驱动视图,数据层面发生变化,导致UI进行重新渲染。数据响应式这部分涉及到的知识点有关依赖收集、依赖触发这两部分,核心思想就是将数据变换成observe的getter和setter形式。 依赖收集 我们都知道在JavaScript中,

weixin_43823603的博客 768
上一篇: 数据结构与算法之递归: LeetCode 93. 复原 IP 地址 (Ts, Py, Go版)
下一篇: Vue3源码梳理:运行时的设计方案概况
Wang's Blog
博客等级 码龄12年 7433粉丝 2421原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值