vue2.x源码 数据响应式原理

版本

Vue.js v2.6.14

Object.definedProperty

主要思想:

  1. 通过Object.defineProperty来劫持data里面的数据
  2. 通过设置get和set属性劫持读取和设置操作

从生命周期入手

在这里插入图片描述

  1. vue在实例化组件的时候会先执行两个生命周期beforeCreate和created
  2. beforeCreate是实例化后执行的第一个生命周期,这时候data ,props,methods等属性没有初始化
  3. 然后会执行initState(vm)后再执行created,这时候属性都完成初始化

initState做了什么

在这里插入图片描述
这里vm对应着每个component,initState是初始化props methods data computed
watch属性,本文关注的是数据的响应式原理,所以重点关注data的初始化过程:

  1. 如果存在data => initData(vm)
  2. 如果不存在data => observe(vm._data = {}, true)

initData(vm)

  function initData (vm) {
    var data = vm.$options.data;
    data = vm._data = typeof data === 'function'
      ? getData(data, vm)
      : data || {};
    if (!isPlainObject(data)) {
      data = {};
      warn(
        'data functions should return an object:\n' +
        'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
        vm
      );
    }
    // proxy data on instance
    var keys = Object.keys(data);
    var props = vm.$options.props;
    var methods = vm.$options.methods;
    var i = keys.length;
    while (i--) {
      var key = keys[i];
      {
        if (methods && hasOwn(methods, key)) {
          warn(
            ("Method \"" + key + "\" has already been defined as a data property."),
            vm
          );
        }
      }
      if (props && hasOwn(props, key)) {
        warn(
          "The data property \"" + key + "\" is already declared as a prop. " +
          "Use prop default value instead.",
          vm
        );
      } else if (!isReserved(key)) {
        proxy(vm, "_data", key);
      }
    }
    // observe data
    observe(data, true /* asRootData */);
  }
  1. 首先是对data的判断,typeof data === “function”,如果是function 通过getData获取返回的值,否则直接用data值,如果不存在则直接用{}
  2. isPlainObject(data)判断是否为对象,不是使用{}
  3. 遍历对象 检查是否存在同名的methods和props
  4. 执行 observe(data, true );

observe

在这里插入图片描述

  1. 先判断不是object或者是VNode,直接返回undefined
  2. 主要的目的是为了data的创建观察者
  3. 如果已经存在观察者,返回原来的观察者

new Observer(value)

  var Observer = function Observer (value) {
    this.value = value;
    this.dep = new Dep();
    this.vmCount = 0;
    def(value, '__ob__', this);
    if (Array.isArray(value)) {
      if (hasProto) {
        protoAugment(value, arrayMethods);
      } else {
        copyAugment(value, arrayMethods, arrayKeys);
      }
      this.observeArray(value);
    } else {
      this.walk(value);
    }
  };
  // 对象走这步
  Observer.prototype.walk = function walk (obj) {
    var keys = Object.keys(obj);
    for (var i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i]);
    }
  };
  //数组走这步
  Observer.prototype.observeArray = function observeArray (items) {
    for (var i = 0, l = items.length; i < l; i++) {
      observe(items[i]);
    }
  };
  1. 给data绑定__ob__属性
  2. 这里分了两步 object和array
  3. 对象会遍历属性执行defineReactive
  4. 数组会遍历执行,劫持操作数组属性protoAugment,对元素再进行创建观察者

defineReactive

 function defineReactive(
    obj,
    key,
    val,
    customSetter,
    shallow
  ) {
    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;
    if ((!getter || setter) && arguments.length === 2) {
      val = obj[key];
    }

    var childOb = !shallow && observe(val);
    Object.defineProperty(obj, key, {
      enumerable: true,
      configurable: true,
      get: function reactiveGetter () {
        var value = getter ? getter.call(obj) : val;
        
        // target 会在执行挂载的时候存在,当执行render函数的时候
        // 因为已经劫持,所以会自动执行此处收集依赖
        // 会初始化 new Watcher 后续另外补充文章
        if (Dep.target) {
          dep.depend();
          if (childOb) {
            childOb.dep.depend();
            if (Array.isArray(value)) {
              dependArray(value);
            }
          }
        }
        return value
      },
      set: function reactiveSetter (newVal) {
        var value = getter ? getter.call(obj) : val;
        /* eslint-disable no-self-compare */
        if (newVal === value || (newVal !== newVal && value !== value)) {
          return
        }
        /* eslint-enable no-self-compare */
        if (customSetter) {
          customSetter();
        }
        // #7981: for accessor properties without setter
        if (getter && !setter) { return }
        if (setter) {
          setter.call(obj, newVal);
        } else {
          val = newVal;
        }
        childOb = !shallow && observe(newVal);
        dep.notify();//通知依赖更新,更新视图
      }
    });
  }
  1. 首先创建依赖 dep = new Dep()
  2. 如果该属性是对象,会执行observe->new Observer()->回到这一步(defineReactive),递归遍历子对象来监听每一个属性
  3. Object.defineProperty劫持监听每一个对象属性
  4. get返回值并且如果Dep.target存在收集依赖
  5. set更新data的属性 val = newVal;
    a. 并堆新的值创建观察者,childOb = !shallow && observe(newVal);
    b. 通知依赖更新 dep.notify();

整体流程

流程图工具:https://app.diagrams.net/
在这里插入图片描述

  1. 主要原理遍历object对象,劫持每一个属性
  2. 依赖的收集会与视图更新相关,会在挂载阶段,针对render上使用到data的属性进行收集依赖
  3. 在更新data上的属性后,会notify通知执行视图,执行diff算法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值