第二章节 响应式原理介绍-【手摸手带你实现一个vue3】

大家好,我是作曲家种太阳,本次的专栏会带你一步步实现一个mini-vue3,每个小节都都回有一些测试,验证当前的一个逻辑,并且我已经把代码上传到github上了,可以根据每个章节去看对应的源码提交记录。

本章介绍循序渐进的介绍vue3的响应式系统,没有需要写的代码,预计30分钟看完

🌟 一、响应系统的概念与意义

响应系统的本质:
数据变化,视图自动更新


🌟 二、为什么需要响应式(JS 的程序性)

默认情况下 JavaScript 的程序性特征:

  • 程序执行流程固定,不会自动随数据变化更新结果。

比如:

js 复制代码
let price = 10
let quantity = 2
let total = price * quantity

console.log('总价格:', total) // 总价格:20

quantity = 5
console.log('总价格:', total) // 总价格:20 (❌期望是50)

此时发现:

  • 当依赖的数据(quantity)变化时,计算结果(total)不会自动更新,违背了我们的期望。

🌟 三、如何实现响应式(effect思想)

最初的思想:

  • 封装计算逻辑到一个函数里(effect),数据变化时重新执行它。
js 复制代码
let total
const effect = () => {
  total = price * quantity
}

effect()
console.log(total) // 20

quantity = 5
effect() // 重新执行
console.log(total) // 50(✅ 符合期望)

但存在明显缺点:必须主动调用 effect。


🌟 四、Vue2 如何实现响应式(Object.defineProperty)

核心 API:

  • Object.defineProperty:监听对象中已有的指定属性的 getter 和 setter

使用示例:

js 复制代码
const product = { price: 10, quantity: 2 }
let total = 0

Object.defineProperty(product, 'quantity', {
  set(newVal) {
    quantity = newVal
    total = product.price * quantity // 自动计算
  },
  get() {
    return quantity
  }
})

特点:

  • 自动响应变化,无需显式调用 effect。

🌟 五、Vue2 响应式设计的缺陷

Vue2 官方文档:

  • 由于 JavaScript 限制,Vue 无法监听对象属性新增、删除,也不能直接监听数组元素变化。

原因:

  • Object.defineProperty 只能监听已存在的属性。
  • 通过数组下标新增元素,或对象新增属性,无法监听到变化。

例子:

js 复制代码
// 对象
product.newProp = '新增属性'  // 不响应

// 数组
arr[5] = '新增元素'          // 不响应

Vue2 提供 Vue.set() 方法解决,但不够优雅。


🌟 六、Vue3 响应式的核心:Proxy

为了克服 Vue2 的不足,Vue3 使用新的响应式核心 API ------ Proxy

Proxy 本质:

  • 代理对象上的所有操作(新增、删除、读取、修改属性)都能被捕获。

示例代码:

js 复制代码
复制编辑
const product = { price: 10, quantity: 2 }

const proxyProduct = new Proxy(product, {
  get(target, key) {
    // 收集依赖(getter)
    return Reflect.get(target, key)
  },
  set(target, key, val) {
    // 触发更新(setter)
    return Reflect.set(target, key, val)
  }
})

proxyProduct.newProp = '响应式新增属性' // ✅响应式!

优势:

  • 不再存在新增属性无法响应的问题。

🌟 七、Proxy 和 Reflect 为什么搭配使用?

Reflect 的作用:

  • 与 Proxy 配合,对对象操作提供标准化的、更加安全可靠的方式。

Reflect 的常见方法:

  • Reflect.get(target, key, receiver)
  • Reflect.set(target, key, value, receiver)
  • Reflect.has(target, key)
  • Reflect.deleteProperty(target, key)

为何需要 Reflect?

  • 如果直接使用 target[key],getter 中的 this 指向原始对象而非 Proxy,这会导致响应性丢失
  • 使用 Reflect 可以明确将 getter 和 setter 操作代理到 Proxy 身上,确保所有访问都经过响应式逻辑处理。

示例说明 Reflect 的必要性:

js 复制代码
复制编辑
const p1 = {
  first: '张',
  last: '三',
  get fullName() {
    return this.first + this.last
  }
}

const proxy = new Proxy(p1, {
  get(target, key, receiver) {
    console.log('getter被触发')
    return Reflect.get(target, key, receiver) // receiver 为 proxy 实例
  }
})

console.log(proxy.fullName)
// getter被触发(fullName)
// getter被触发(first)
// getter被触发(last)
  • 如果不用 Reflect,只有第一次被触发,内部的属性调用不再触发 getter。

🌟 八、Vue 响应系统总结(Vue2 与 Vue3 对比)

特点 Vue 2 (Object.defineProperty) Vue 3 (Proxy + Reflect)
对象已有属性响应性 ✅ 可以监听 ✅ 可以监听
新增属性的响应性 ❌ 无法监听(需用 Vue.set) ✅ 自动响应
数组元素新增 ❌ 无法直接响应(需用Vue.set) ✅ 自动响应
删除属性 ❌ 无法监听 ✅ 可以监听
性能与实现复杂度 中等(需额外API) 更优(统一API)
IE兼容性 ✅ 支持 IE9+ ❌ Proxy 不支持 IE11

为什么 Vue3 放弃 Object.defineProperty 改用 Proxy?

因为 Proxy 能代理整个对象的所有操作,解决了 Vue2 中无法监听新增或删除属性的问题,同时配合 Reflect 能保证所有 getter/setter 的内部调用都通过 Proxy,避免响应性丢失。

相关推荐
莫空0000几秒前
深入理解JavaScript属性描述符:从数据属性到存取器属性
前端·面试
guojl1 分钟前
深度剖析Kafka读写机制
前端
FogLetter2 分钟前
图片懒加载:让网页飞起来的魔法技巧 ✨
前端·javascript·css
Mxuan2 分钟前
vscode webview 插件开发(精装篇)
前端
Mxuan3 分钟前
vscode webview 插件开发(交付篇)
前端
Mxuan5 分钟前
vscode 插件与 electron 应用跳转网页进行登录的实践
前端
拾光拾趣录5 分钟前
JavaScript 加载对浏览器渲染的影响
前端·javascript·浏览器
Codebee5 分钟前
OneCode图表配置速查手册
大数据·前端·数据可视化
然我6 分钟前
React 开发通关指南:用 HTML 的思维写 JS🚀🚀
前端·react.js·html
Mxuan7 分钟前
vscode webview 插件开发(毛坯篇)
前端