Vue2响应式原理源码解读

这里先铺垫一下vue的渲染系统,该模块主要包含三个功能:
功能一:h函数,用于返回一个VNode对象;
功能二:mount函数,用于将VNode挂载到DOM上;
功能三:patch函数,用于对两个VNode进行对比,决定如何处理新的VNode;
h函数的实现:
直接返回一个VNode对象即可
在这里插入图片描述
Mount函数 – 挂载VNode
第一步:根据tag,创建HTML元素,并且存储
到vnode的el中;
第二步:处理props属性
如果以on开头,那么监听事件;
普通属性直接通过 setAttribute 添加即可;
第三步:处理子节点
如果是字符串节点,那么直接设置textContent;
如果是数组节点,那么遍历调用 mount 函数;

在这里插入图片描述

Vue2响应式原理就是Object.defineProperty结合发布订阅模式

总共分为三步骤:
init 阶段: VUE 的 data的属性都会被reactive化,也就是加上 setter/getter函数
其中这里的Dep就是一个观察者类,每一个data的属性都会有一个dep对象。当getter调用的时候,去dep里注册函数,
至于注册了什么函数,我们等会再说。
setter的时候,就是去通知执行刚刚注册的函数。

mount 阶段:
mount 阶段的时候,会创建一个Watcher类的对象。这个Watcher实际上是连接Vue组件与Dep的桥梁。
每一个Watcher对应一个vue component。
这里可以看出new Watcher的时候,constructor 里的this.getter.call(vm, vm)函数会被执行。getter就是updateComponent。这个函数会调用组件的render函数来更新重新渲染。

mountComponent(vm: Component, el: ?Element, ...) {
    vm.$el = el

    ...

    updateComponent = () => {
      vm._update(vm._render(), ...)
    }

    new Watcher(vm, updateComponent, ...)
    ...
}

class Watcher {
  getter: Function;

  // 代码经过简化
  constructor(vm: Component, expOrFn: string | Function, ...) {
    ...
    this.getter = expOrFn
    Dep.target = this                      // 注意这里将当前的Watcher赋值给了Dep.target
    this.value = this.getter.call(vm, vm)  // 调用组件的更新函数
    ...
  }
}

而render函数里,会访问data的属性,比如

render: function (createElement) {
  return createElement('h1', this.blogTitle)
}

此时会去调用这个属性blogTitle的getter函数,即:

// getter函数
get: function reactiveGetter () {
    ....
    dep.depend()
    return value
    ....
 },

// dep的depend函数
depend () {
    if (Dep.target) {
      Dep.target.addDep(this)
    }
}

在depend的函数里,Dep.target就是watcher本身,这里做的事情就是给blogTitle注册了Watcher这个对象。这样每次render一个vue 组件的时候,如果这个组件用到了blogTitle,那么这个组件相对应的Watcher对象都会被注册到blogTitle的Dep中。
这个过程就叫做依赖收集。
收集完所有依赖blogTitle属性的组件所对应的Watcher之后,当它发生改变的时候,就会去通知Watcher更新关联的组件。
3、更新阶段:
当blogTitle 发生改变的时候,就去调用Dep的notify函数,然后通知所有的Watcher调用dep
的update函数更新。
在这里插入图片描述
每个组件对应一个watcher,当某个组件内使用了响应式变量以后,响应式变量都有一个dep类会收集组件的watcher,当组件发生变化以后dep会调用组件的watcher里的getter方法进行对应组件的更新
总结:
挂载前,先给每一个Data属性进行遍历,注册getter,setter,也就是reactive化。在getter中维护了一个用来收集依赖的对象。挂载中,一是把template/el转成render函数。二是创建一个updateComponent函数,在这个函数中会调用上一步的render函数。三(依赖收集过程)是给每个组件实例new 一个自己的Watcher对象,然后把updateComponent函数传入,此时watcher会立即调用这个函数,然后会调用组件的render函数去生成虚拟DOM。在调用render的时候,就会需要用到data的属性值,此时会触发getter函数,在getter内部将当前的Watcher函数注册进sub里,进而完成依赖收集挂载后,当data属性发生改变之后,触发setter,就会遍历sub里所有的watcher对象,通知它们去重新渲染组件。
在这里插入图片描述

作用详解:
getter 方法完成的工作就是依赖收集 —— dep.depend()
setter 方法完成的工作就是发布更新 —— dep.notify()
Watcher(订阅者):有get和update方法
调度中心作用的 Dep:
1.收集订阅者 Watcher 并添加到观察者列表 subs
2.接收发布者的事件
3.通知订阅者目标更新,让订阅者执行自己的 update 方法
vue组件1->watcher1->dep
vue组件2->watcher2->dep
watcher是连接vue组件和dep的桥梁
每个组件对应一个watcher,每个属性对应一个dep
data1->name:‘wyh’->dep
data2->name:‘aaa’->dep
dep中getter和setter有两个方法
getter->dep.depend()收集依赖
setter_>dep.notify()通知更新

Vue3与vue2的响应式区别:
一:
Object.definedProperty 是劫持对象的属性时,如果新增元素:
那么Vue2需要再次 调用definedProperty,而 Proxy 劫持的是整个对象,不需要做特殊处理;
二:
Proxy 能观察的类型比 defineProperty 更丰富
has:in操作符的捕获器;
deleteProperty:delete 操作符的捕捉器;

defineProperty缺点

新添加的属性不是响应式,解决方案使用$set
需要深度遍历对象依次添加响应式,性能消耗大,解决方案–Proxy
通过数组下标修改无法更新视图

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值