vue源码分析-响应式系统(二)

本文深入探讨Vue.js响应式系统,重点分析data的响应式处理,包括问题思考、依赖收集和派发更新。同时,文章讨论了computed属性的依赖收集和派发更新,揭示了计算属性如何优化性能。在依赖收集阶段,当访问数据时,会将当前场景作为依赖收集到数据的依赖管理器中。在数据更新时,依赖管理器会通知并更新对应的依赖。计算属性的值会被缓存,只有相关响应式数据变化时才会重新计算。
摘要由CSDN通过智能技术生成

为了深入介绍响应式系统的内部实现原理,我们花了一整节的篇幅介绍了数据(包括data, computed,props)如何初始化成为响应式对象的过程。有了响应式数据对象的知识,上一节的后半部分我们还在保留源码结构的基础上构建了一个以data为数据的响应式系统,而这一节,我们继续深入响应式系统内部构建的细节,详细分析Vue在响应式系统中对data,computed的处理。

7.8 相关概念

在构建简易式响应式系统的时候,我们引出了几个重要的概念,他们都是响应式原理设计的核心,我们先简单回顾一下:

  • Observer类,实例化一个Observer类会通过Object.defineProperty对数据的getter,setter方法进行改写,在getter阶段进行依赖的收集,在数据发生更新阶段,触发setter方法进行依赖的更新
  • watcher类,实例化watcher类相当于创建一个依赖,简单的理解是数据在哪里被使用就需要产生了一个依赖。当数据发生改变时,会通知到每个依赖进行更新,前面提到的渲染wathcer便是渲染dom时使用数据产生的依赖。
  • Dep类,既然watcher理解为每个数据需要监听的依赖,那么对这些依赖的收集和通知则需要另一个类来管理,这个类便是Dep,Dep需要做的只有两件事,收集依赖和派发更新依赖。

这是响应式系统构建的三个基本核心概念,也是这一节的基础,如果还没有印象,请先回顾上一节对极简风响应式系统的构建

7.9 data

7.9.1 问题思考

在开始分析data之前,我们先抛出几个问题让读者思考,而答案都包含在接下来内容分析中。

  • 前面已经知道,Dep是作为管理依赖的容器,那么这个容器在什么时候产生?也就是实例化Dep发生在什么时候?

  • Dep收集了什么类型的依赖?即watcher作为依赖的分类有哪些,分别是什么场景,以及区别在哪里?

  • Observer这个类具体对getter,setter方法做了哪些事情?

  • 手写的watcher和页面数据渲染监听的watch如果同时监听到数据的变化,优先级怎么排?

  • 有了依赖的收集是不是还有依赖的解除,依赖解除的意义在哪里?

带着这几个问题,我们开始对data的响应式细节展开分析。

7.9.2 依赖收集

data在初始化阶段会实例化一个Observer类,这个类的定义如下(忽略数组类型的data):

// initData 
function initData(data) {
   
  ···
  observe(data, true)
}
// observe
function observe(value, asRootData) {
   
  ···
  ob = new Observer(value);
  return ob
}

// 观察者类,对象只要设置成拥有观察属性,则对象下的所有属性都会重写getter和setter方法,而getter,setting方法会进行依赖的收集和派发更新
var Observer = function Observer (value) {
   
    ···
    // 将__ob__属性设置成不可枚举属性。外部无法通过遍历获取。
    def(value, '__ob__', this);
    // 数组处理
    if (Array.isArray(value)) {
   
        ···
    } else {
   
      // 对象处理
      this.walk(value);
    }
  };

function def (obj, key, val, enumerable) {
   
  Object.defineProperty(obj, key, {
   
    value: val,
    enumerable: !!enumerable, // 是否可枚举
    writable: true,
    configurable: true
  });
}

Observer会为data添加一个__ob__属性, __ob__属性是作为响应式对象的标志,同时def方法确保了该属性是不可枚举属性,即外界无法通过遍历获取该属性值。除了标志响应式对象外,Observer类还调用了原型上的walk方法,遍历对象上每个属性进行getter,setter的改写。

Observer.prototype.walk = function walk (obj) {
   
    // 获取对象所有属性,遍历调用defineReactive###1进行改写
    var keys = Object.keys(obj);
    for (var i = 0; i < keys.length; i++) {
   
        defineReactive###1(obj, keys[i]);
    }
};

defineReactive###1是响应式构建的核心,它会先实例化一个Dep类,即为每个数据都创建一个依赖的管理,之后利用Object.defineProperty重写getter,setter方法。这里我们只分析依赖收集的代码。

function defineReactive###1 (obj,key,val,customSetter,shallow) {
   
    // 每个数据实例化一个Dep类,创建一个依赖的管理
    var dep = new Dep();

    var property = Object.getOwnPropertyDescriptor(obj, key);
    // 属性必须满足可配置
    if (property && property.configurable === false) {
   
      return
    }
    // cater for pre-defined getter/setters
    var getter = property && property.get;
    var setter = property && property.set;
    // 这一部分的逻辑是针对深层次的对象,如果对象的属性是一个对象,则会递归调用实例化Observe类,让其属性值也转换为响应式对象
    var childOb = !shallow && observe(val);
    Object.defineProperty(obj, key, {
   
      enumerable: true,
      configurable: true,s
      get: function reactiveGetter () {
   
        var value = getter ? getter.call(obj) : val;
        if (Dep.target) {
   
          // 为当前watcher添加dep数据
          dep.depend();
          if (childOb) {
   
            childOb.dep.depend();
            if (Array.isArray(value)) {
   
              dependArray(value);
            }
          }
        }
        return value
      },
      set: function reactiveSetter (newVal) {
   }
    });
  }

主要看getter的逻辑,我们知道当data中属性值被访问时,会被getter函数拦截,根据我们旧有的知识体系可以知道,实例挂载前会创建一个渲染watcher

new Watcher(vm, updateComponent, noop, {
   
  before: function before () {
   
    if (vm._isMounted && !vm._isDestroyed) {
   
      callHook(vm, 'beforeUpdate');
    }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值