历史最全vue2/3!!响应式原理

  • vue2 响应式原理

Vue.js 是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty()来劫持各个属性的 setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。主要分为以下几个步骤:

  1. 需要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter 这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化
  2. compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
  3. Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,主要做的事情是: ① 在自身实例化时往属性订阅器(dep)里面添加自己 ② 自身必须有一个 update()方法 ③ 待属性变动 dep.notice()通知时,能调用自身的 update()方法,并触发 Compile 中绑定的回调,则功成身退。
  4. MVVM 作为数据绑定的入口,整合 Observer、Compile 和 Watcher 三者,通过 Observer 来监听自己的 model 数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据 model 变更的双向绑定效果。

  • vue3 响应式原理

Vue3 使用 Proxy 来监控数据的变化。Proxy 是 ES6 中提供的功能,其作用为:用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等).相对于 Object.defineProperty() 其有以下特点:Proxy 直接代理整个对象而非对象属性,这样只需做一层代理就可以监听同级结构下的所有属性变化,包括新增属性和删除属性。

Proxy 可以监听数组的变化。

怎么理解响应式?

总结一下:

  1. 任何一个 Vue Component 都有一个与之对应的 Watcher 实例。
  2. Vue 的 data 上的属性会被添加 getter 和 setter 属性。
  3. 当 Vue Component render 函数被执行的时候, data 上会被 触碰(touch), 即被getter 方法会被调用, 此时 Vue 会去记录此 Vue component 所依赖的所有 data。(这一过程被称为依赖收集)
  4. data 被改动时(主要是用户操作), 即被setter 方法会被调用, 此时 Vue 会去通知所有依赖于此 data 的组件去调用他们的 render 函数进行更新

响应性是一种可以使我们声明式地处理变化的编程范式

let A0 = 1
let A1 = 2
let A2 = A0 + A1

console.log(A2) // 3

A0 = 2
console.log(A2) // 仍然是 3

分析

let A2

function update() {
  A2 = A0 + A1
}

当 A0, A1 的值改变时,就去自动执行 update

然后,我们需要定义几个术语:

  • 这个 update() 函数会产生一个副作用或者就简称为作用因为它会更改程序里的状态。
  • A0 和 A1 被视为这个作用的依赖因为它们的值被用来执行这个作用。因此这次作用也可以说是一个它依赖的订阅者

我们无法直接追踪对上述示例中局部变量的读写过程,在原生 JavaScript 中没有提供这样一种机制。但是我们是可以追踪一个对象的属性进行读和写的。

Object.defineProperty

v2 的用法

基本使用

Object.defineProperty(obj, 'a', {
  set(val) {
    console.log('obj.a被赋值...')
  },
  get() {
    console.log('obj.a被访问...')
  },
})

封装函数

function defineReactive(obj, key, value) {
  Object.defineProperty(obj, key, {
    set(val) {
      console.log('obj.a被赋值...')
      value = val
    },
    get() {
      console.log('obj.a被访问...')
      return value
    },
  })
}
let obj = { a: 1, b: 2 }
defineReactive(obj, 'a', obj['a'])

循环+递归

function defineReactive(obj, key, value) {
  Object.defineProperty(obj, key, {
    set(val) {
      console.log(key, '被赋值...')
      value = val
    },
    get() {
      console.log(key, '被访问...')
      return value
    },
  })
}
var obj = { a: 1, b: 2, c: { c1: 0 } }
function walk(obj) {
  for (let key in obj) {
    let value = obj[key]
    if (typeof value === 'object') {
      walk(value)
    } else {
      defineReactive(obj, key, value)
    }
  }
}

walk(obj)

v2 的问题

  1. 深度监听需要一次性递归;
  2. 无法监听对象新增|删除的属性
  3. 无法监听数组的元素操作

Proxy

可以有效解决上面的三个问题

let obj = { a: 1, b: 2, c: { c1: 0 } }

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key) {
      console.log(key, '被访问')
      let value = target[key]
      if (typeof value === 'object') {
        return reactive(value)
      } else {
        return target[key]
      }
    },
    set(target, key, newValue) {
      target[key] = newValue
      console.log(key, '被设置')
    },
  })
}
let newObj = reactive(obj)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值