vue响应式在源码的的实现

前端开发最重要的 2 个⼯作:

  • ⼀个是把数据渲染到⻚⾯
  • 另⼀个是处理⽤户交互

1、在 Vue 的初始化阶段, _init ⽅法执⾏的时候, 会执⾏ initState(vm) ⽅法

1、初始化props

调⽤ defineReactive ⽅法把每个 prop 对应的值变成响应式。
⼀旦对象拥有了 getter 和 setter, 我们可以简单地把这个对象称为响应式对象。

defineReactive(props, key, value)

通过 proxy把 vm._props.xxx 的访问代理到 vm.xxx 上

 proxy(vm, `_props`, key)

2、初始化data

1、对定义 data 函数返回对象的遍历, 通过 proxy把每⼀个值 vm._data.xxx 都代理到 vm.xxx 上。

while (i--) {
    const key = keys[i]
。。。
proxy(vm, `_data`, key)
。。。
}

proxy的作⽤是把 props 和 data 上的属性代理到 vm 实例上

function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}
  • 第一个参数target是vm实例
  • 第二个参数sourceKey是字符串 _data(在执行proxy之前已经让vm._data 等于data函数返回的对象了)
  • 第三个参数key就是data中的key

2、调⽤ observe ⽅法观测整个 data的变化, 把 data 也变成响应式

observe(data, true /* asRootData */)

observe 的功能就是⽤来监测数据的变化

 function observe (value: any, asRootData: ?boolean): Observer | void {
  let ob: Observer | void
    。。。
    ob = new Observer(value)
	。。。
  return ob
}

先对传入的value做一些判断逻辑(为简化我把这些都删了)
最后传入data对象给new Observer,生成observer实例

1、Observer类

在Observer类的构造函数中:

1、生成了一个dep实例
this.dep = new Dep()
2、对 value 做判断(value 是vue实例中的data数据对象)
1、如果是数组

调⽤ observeArray ⽅法,observeArray 是遍历数组再次调⽤ observe ⽅法

2、如果是对象

对纯对象调⽤ walk ⽅法,

 this.walk(value)

walk ⽅法是遍历对象的 key 调⽤ defineReactive ⽅法

walk ⽅法是遍历对象的 key 调⽤ defineReactive ⽅法
 walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

defineReactive 的功能就是定义⼀个响应式对象, 给对象动态添加 getter 和 setter,
在defineReactive 方法中:

  • 1、为每个数据创建一个dep实例,它是用于watcher收集的,它有收集watcher、通知watcher的方法。
const dep = new Dep()
  • 2、然后对⼦对象递归调⽤ observe ⽅法, 这样就保证了⽆论 obj 的结构多复杂, 它的所有⼦属性也能变成响应式的对象, 这样我们访问或修改 obj 中⼀个嵌套较深的属性, 也能触发 getter 和 setter。
  • 3、Object.defineProperty 去给 每个属性值 添加 getter 和 setter
    在get中依赖(watcher)收集
    在set中派发更新
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get:
set:
}

3、依赖收集

get: function reactiveGetter () {
      const value = getter ? getter.call(obj) : val
      if (Dep.target) {
        dep.depend()
        if (childOb) {
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
1、什么时候触发getter

当对数据对象的访问会触发他们的 getter ⽅法, 那么这些对象什么时候被访问呢?
执⾏ vm._render() ⽅法,这个⽅法会⽣成 渲染 VNode, 并且在这个过程中会对 vm 上的数据访问, 这个时候就触发了数据对象的 getter。

2、触发getter后执行dep.depend()

每个数据值的 getter 都持有⼀个 dep , 在触发 getter 的时候会调⽤ dep.depend() ⽅法, 也就会执⾏ Dep.target.addDep(this),Dep.target是当前的渲染watcher实例,把这个watcher实例放入了这个被使用的数据的dep数组中

4、派发更新

在触发setter时,会执行dep实例的notify方法;

set: function reactiveSetter (newVal) {
    。。。
      dep.notify()
    }

notify方法遍历所有的 subs , 也就是 Watcher 的实例数组, 然后调⽤每⼀个watcher 实例的 update ⽅法

notify () {
const subs = this.subs.slice()
    。。。
    for (let i = 0, l = subs.length; i < l; i++) {
      subs[i].update()
    }
  }

Vue 在做派发更新的时候, 它并不会每次数据改变都触发 watcher 的回调, ⽽是把这些 watcher 先添加到⼀个队列⾥, 然后在 nextTick 后执⾏。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值