Vue变化响应

响应式侦测

1. 对象侦测

Vue2 实现方式

Vue2 使用 Object.defineProperty 来检测对象的变化。

javascript 复制代码
let car = {}
let val = 3000

Object.defineProperty(car, 'price', {
  enumerable: true,
  configurable: true,
  get() {
    console.log('price属性被读取了')
    return val
  },
  set(newVal) {
    console.log('price属性被修改了')
    val = newVal
  }
})

Vue2 通过递归的 Observer 将对象中的所有属性转换为可观测对象

在 getter 中收集依赖,在 setter 中通知依赖更新

依赖:数据变化的时候会去通知视图,但是不能全局去通知,所以要收集谁使用了这个数据,这个谁,就是依赖

在vue中,谁得到数据谁就是依赖,会给谁创建一个Water实例,用这个实例来代表依赖,由Water去通知真正的依赖,上方那个update就是Water的东西

vue在getter里面收集依赖,在setter里面通知依赖去进行更新

vue为每个数据都建立了一个依赖管理器,把这个数据的所有依赖都管理起来

依赖管理器实现:

javascript 复制代码
// 依赖管理器
export default class Dep {
  constructor() {
    this.subs = []
  }
  
  addSub(sub) {
    this.subs.push(sub)
  }
  
  removeSub(sub) {
    const index = this.subs.indexOf(sub)
    if (index > -1) {
      this.subs.splice(index, 1)
    }
  }
  
  depend() {
    if (window.target) {
      this.addSub(window.target)
    }
  }
  
  notify() {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update() // 通知依赖更新
    }
  }
}

流程:

数据通过监听器转换成拥有 getter 和 setter 的形式

外界通过 Watcher 读取数据,触发 getter 将 Watcher 添加到依赖数组

数据变化时,setter 调用 Watcher(依赖)发送通知

Watcher 最终通知外界,引发视图更新或其他操作

Vue3 实现方式

Vue3 使用 Proxy 来检测变化。

  • 可以直接监听整个对象,无需递归遍历
  • 可以监听新增和删除的属性
  • 性能更好

2. 数组侦测

Vue2 实现方式

数组无法直接使用 Object.defineProperty 进行侦测。

  1. 封装数组的原生方法,使其能够被侦测
  2. 使用变异方法(mutator methods)来触发更新

Array原型中可以改变数组自身内容的方法有7个,分别是push,pop,shift,unshift,splice,sort,reverse

实现原理:

  • 创建继承自 Array.prototype 的新对象
  • 重写上述7个方法,在调用原生方法后手动触发更新通知
  • 数组依然使用 getter 收集依赖,但通过封装的数组方法感知变化

不足的地方在于,通过下标操作的时候,vue无法监听到,所以vue提供了Vue.set和Vue.delete这几个方法

Vue3 实现方式

Vue3 使用 Proxy 可以监听到数组的所有变化。

  • 可以直接监听数组下标操作
  • 可以监听数组长度的变化
  • 无需特殊处理数组方法

示例:

javascript 复制代码
const array = [1, 2, 3]
const proxy = new Proxy(array, {
  set(target, property, value, receiver) {
    console.log(`数组的${property}属性被修改为${value}`)
    return Reflect.set(target, property, value, receiver)
  }
})

总结对比

特性 Vue2 (Object.defineProperty) Vue3 (Proxy)
对象监听 需要递归遍历对象属性 直接监听整个对象
新增属性 无法自动侦测,需要Vue.set 自动侦测
删除属性 无法自动侦测,需要Vue.delete 自动侦测
数组监听 重写7个数组方法 直接监听所有变化
数组下标操作 无法侦测 可以侦测
性能 递归初始化成本高 惰性监听,性能更好
相关推荐
子兮曰3 小时前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 小时前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万4 小时前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝4 小时前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋4 小时前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁5 小时前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95277 小时前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大7 小时前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师7 小时前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学8 小时前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端