Vue数据代理


前言

vue数据代理的学习笔记


一、问题

首先对data进行读操作


const vm = new Vue({
	el: "#test",
	data:{
		name: "fubuki",
	}
})
console.log(vm.name === vm._data.vm)// true
console.log(vm)

输出结果看到在vm对象中多了name属性,且name的值和vm本身的_data属性中name的值相同
在这里插入图片描述
那么vue是如何把传入的data里的属性传给vm对象本身的呢?vm.name和vm._data.name又是什么关系呢?


二、源码分析

1.绑定属性

vm.$options = options// 将整个Vue参数对象传入$options属性
var data = vm.$options.data// 取出data值
/*data = vm._data = typeof data === 'function' 
	? getData(data, vm)
    : data || {};*/ // 对函数和对象类型的data进行不同处理
var keys = Object.keys(data);
while (i--) {
    var key = keys[i];
	if (!isReserved(key)) {// 如果当前属性不是$或_开头,则将该属性添加进vm对象_data属性中,
	      proxy(vm, "_data", key);
	    }
observe(data, true /* asRootData */);// 给data加观察,通过defineReactive中的defineProperty给每个key修改get/set方法

接着在观察过程中如果触发了data中的get特性(set特性类似),则自动为vm对象添加属性值(即vm.name),其值为下面的返回值value

get: function reactiveGetter () {
      var value = getter ? getter.call(obj) : val;
      if (Dep.target) {// dep不做讨论,就是被观察对象触发get时有反馈
        dep.depend();// 调用depend会调用该Watcher实例的addDep方法,不做讨论
        if (childOb) {// 忽略
          childOb.dep.depend();
          if (Array.isArray(value)) {// value不是数组,略
            dependArray(value);
          }
        }
      }
      return value
    },

2.验证

将上述代码返回值value改为’aqua’,则输出如下
在这里插入图片描述

可见vm.name与vm._data.name同时改变(因为data和vm._data是同一个地址)

3.源码补充

①defineProperty

//对象已有的属性添加特性描述
Object.defineProperty(obj,"test",{// 若已有属性则修改,没有则添加
	value:任意类型,// 属性对应的值,默认undefined
    configurable:true | false,// 是否可修改属性特性(使用defineProperty重配置)
    enumerable:true | false,// 是否可遍历(eg. Object.keys())
    writable:true | false// 是否可修改属性(不同于configurable,直接vm.name修改)
    /*get
    set*/
});

②noop空函数

// 函数初始化时可用,后续不需判断callback类型,直接调用callback()
if(typeof callback != 'function')
	callback = noop

③isReserved

function isReserved (str) {
  var c = (str + '').charCodeAt(0);
  return c === 0x24 || c === 0x5F// 0x24=>'$',0x5F=>'_'
}

④observe

function observe (value, asRootData) {// value = data,asRootData = true
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  var ob;
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {// 判断是否被观察到
    ob = value.__ob__;
  } else if (
    observerState.shouldConvert &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value);//不符合条件则添加观察
  }
  if (asRootData && ob) {// 看不懂
    ob.vmCount++;
  }
  return ob
}

⑤Observer

var Observer = function Observer (value) {// 此时value为data
  this.value = value;
  this.dep = new Dep();
  this.vmCount = 0;
  def(value, '__ob__', this);// 向data内添加'__ob__'属性,作为已经被观察的标志,值为Observer对象(注意此时存在递归)
  if (Array.isArray(value)) {// 如果value是数组,则对每个元素进行观察
    var augment = hasProto// hasProto = '__proto__' in {} #居然能这么写!
      ? protoAugment
      : copyAugment;
    augment(value, arrayMethods, arrayKeys);
    /*各参数定义如下
    var arrayProto = Array.prototype;
	var arrayMethods = Object.create(arrayProto);以arrayProto为原型对象创造空对象,然后增添各种方法,不详细展开
	var arrayKeys = Object.getOwnPropertyNames(arrayMethods);返回arrayMethods对象的所有属性(不同于Object.keys())
	*/
    this.observeArray(value);// 遍历,对数组每个元素调用observe
  } else {// 如果value是对象,则对其每个key调用defineReactive来获得该key的set/get控制权
    this.walk(value);
  }
};

⑥copyAugment

function copyAugment (target, src, keys) {// 向data中添加先前定义的一些属性,不细说
  for (var i = 0, l = keys.length; i < l; i++) {
    var key = keys[i];
    def(target, key, src[key]);
  }
}

⑦protoAugment

function protoAugment (target, src, keys) {
  /* eslint-disable no-proto */
  target.__proto__ = src;// 看不懂
  /* eslint-enable no-proto */
}

⑧walk

Observer.prototype.walk = function walk (obj) {// 相当重要!为data每个key调用defineReactive
  var keys = Object.keys(obj);
  for (var i = 0; i < keys.length; i++) {
    defineReactive(obj, keys[i], obj[keys[i]]);
  }
};

三、总结

核心部分很简单,首先取出data并绑定vm._data对象和被观察对象var data(此时vm._data内只有’__ob__’),接着通过添加observer并修改属性的get和set特性,进而同步添加输入的属性,实现外层对象数据代理(vm.name === vm._data.name)


小p第一次写博客,难免漏洞百出,以后要更加努力啊!

四、补充

Dep、Watcher、Array methods

原理是当有get/set动作时,Dep通知Watcher执行相应程序,具体过程怎样以后有时间再总结吧



看到vm属性和vm._data里还有name属性对应的get和set方法,暂时就不写啦
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值