Vue实例创建之data处理和挂载

前言

上篇文章是Vue初始化加载的过程,主要的处理是对Vue实例方法以及静态方法的定义。
本篇是分析调用Vue构造函数的过程,实际上是主要分析大致流程、props、methods、data。

具体逻辑

使用Vue构造函数创建Vue实例,具体的处理逻辑如下图:
这里写图片描述

从上图中可以看出,Vue实例的具体处理是定义相关的内部属性包括props、data、methods、computed都会在创建实例时被处理,还有beforeCreate、created生命周期函数的执行。

本篇就具体initState以及$mount函数的执行逻辑。

initState执行逻辑

initState的具体处理逻辑如下图所示:
这里写图片描述

从上面中可以看出initState的处理逻辑是处理组件中的props、data、computed、watch、methods,从上面可以推测响应式data以及compute都是在对应的方法中处理,这部分也是主要处理逻辑。

本文主要是针对于data部分的具体分析。

data部分

data部分的处理实际上是在initState部分,具体如下:

opts.data ? initData(vm) : observe(vm._data = {}, true);

首先看看initData的具体处理:
这里写图片描述

从上图中的逻辑处理中可以看出,实际上initData的主要处理:

  • data为函数或对象的处理
  • 判断data中定义的key是否在methods、props中同名,同名就报错
  • 定义_data并使用内部定义的proxy方法代理所有属性
  • 调用observe来观察data中所有属性

上面需要主要关注的点:

  • getData中具体处理
  • proxy(vm, ‘_data’, key)的具体处理
  • observe(data, true):实现data响应式的具体处理

getData
实际上该方法主要执行就是调用data,即data.call(vm, vm)

proxy
实际上该方法就是使用Object.defineProperty实现数据监听,但是是在_data对象上,_data是Vue内data的拷贝,具体处理如下:

var proxy = function(target, source, key) {
    sharedPropertyDefinition.get = function proxyGetter () {
      return this[sourceKey][key]
    };
    sharedPropertyDefinition.set = function proxySetter (val) {
      this[sourceKey][key] = val;
    };
    Object.defineProperty(target, key, sharedPropertyDefinition);
};

从上面可以看出这边是对将data中属性拷贝到_data上,并实现监听

observe
这是实现data响应式的关键处理,实际上这边主要的处理如下:

var ob = new Observer(value);
return ob;

即observe是用于创建观察观察者对象,用于观察data中数据。

接下来就具体分析Observer对象创建过程:
这里写图片描述

从上面的处理逻辑中可以看到对于data中数组和非数组类型有不同的处理,分别是observeArray和walk函数的处理。

observeArray的处理实际上是:

递归将Array中元素平铺成非数组来处理,即对数组中每个元素都调用observe函数
walk中的处理实际上是:
循环遍历data中属性,调用defineReactive函数来实现监听

defineReactive
这是Vue中是实现监听的函数,主要就是Object.defineProperty拦截数据定义响应式,不过该函数还有一些具体的处理的逻辑,具体如下:
这里写图片描述

其中set和get中的处理就是响应式实现的处理,而set中有一点需要关注是通知视图更新,而这实现依赖dep对象,dep对象中保存了Watcher对象集合,通过notify实现视图更新处理,即:

  • this.text = ‘world’ 会触发set函数
  • set函数中使用dep.notify()来实现视图更新处理

而Watcher对象的创建,实际上是new Vue()实例最后挂载时处理的,即el或$mount时处理的。

el或$mount挂载

对于挂载的处理是在_init即Vue()构造函数中调用的,具体的处理就是调用$mount方法,如下:

if (vm.$options.el) vm.$mount(vm.$options.el);

而$mount是定义在Vue原型上的实例方法,这也解释了Vue挂载的两种方式:

  • el形式,例如: new Vue({ el: ‘#app’})
  • m o u n t : n e w V u e ( ) . mount(‘#app’)

$mount方法中具体处理如下:
这里写图片描述

从上面可以得出知晓下面几个结论:

  • el和 m o u n t e l mount方法
  • 若render函数存在,源码中会将template/el 转换为render函数
  • 挂载点应该是普通的html元素,而不应该是html或body这种特殊的
  • 如果template不存在,实际上会获取包含挂载点在内的outerHTML部分作为template

$mount内部是调用mountComponent函数,该函数的具体处理是什么呢?具体处理逻辑如下图:
这里写图片描述

从上图逻辑处理中可知:

  • mountComponent中实际上处理beforeMount、mounted
  • 每个Vue实例都会有相应的Watcher实例对应,是Watcher构造函数中vm._watcher和vm._watchers记录了当前实例和watcher对象集合

Vue响应式原理

下面是Vue.js官网给出的响应式的具体流程:
这里写图片描述

通过上面中defineReactive中可知在set函数中会通过dep.notify()通知视图更新,但是没有分析get中的具体处理,接下来解释getter中是Watcher是如何收集依赖的。

var value = getter ? getter.call(obj) : val;
// new Watcher中会触发视图渲染,实际上是底层还是调用Vue原型上
// 的实例方法_render,_render中会调用data中数据触发getter
// 此处Dep.Target是Watcher对象
if (Dep.target) {
   dep.depend();
   if (childOb) {
      childOb.dep.depend();
      if (Array.isArray(value)) {
        dependArray(value);
      }
    }
  }
return value

Watcher是如何收集依赖呢?实际上就是上面dep.depend方法,Dep.target当前指向Watcher对象,dep.depend方法内部实际上是:

Dep.prototype.depend = function() {
    Dep.target && Dep.target.addDep(this);
}

即会调用Watcher的实例对象addDep,看看addDep实例方法的具体处理:

Watcher.prototype.addDep = function addDep (dep) {
  var id = dep.id;
  // 是否已存在
  if (!this.newDepIds.has(id)) {
    this.newDepIds.add(id);
    // 这里Watcher建立与依赖对象dep的联系
    this.newDeps.push(dep);
    if (!this.depIds.has(id)) {
      // 此处是进行赋值时视图更新的关键,setter中会触发dep.notify,实际上notify内部是遍历subs属性,该属性此时存储的就是Watcher对象,即通知Watcher对象渲染视图
      dep.addSub(this);
    }
  }
};

总结

本文只是针对data和挂载的流程做了分析:

  • 对于new Vue()初始化过程有了了解
  • 对于data的处理过程有了了解:

    initState -> initData -> observe -> new Observer() -> Observer.prototype.walk -> defineReactive()
    而defineReactive是整个响应式实现的关键即Object.defineProperty中get和set函数具体处理,而set中会调用dep.notify()通知视图更新,而get中会建立依赖对象Dep与Watcher对象之间的关系

  • el和$mount两种挂载背后的处理:

    $mount -> mountComponent -> new Watcher() -> _render()执行渲染视图

猜你喜欢

转载自blog.csdn.net/s1879046/article/details/82151666