Vuevue收藏

48.vue响应式系统构建过程->initData

2022-02-10  本文已影响0人  wo不是黄蓉

我的vue版本 -> Vue.js v2.6.11

概述:vue响应式系统构建过程主要是在Init阶段,在initState()方法中会对propsmethodsdatacomputedwatcher等内容进行初始化,在初始化data阶段会对传入的options中的data进行一些校验,接下来就是使用new Observer()data中的数据进行响应化处理

1.src\core\instance\index.js -> vue源码的入口文件

//入口
function Vue(options) {
  //初始化vue
  this._init(options);
}

2.src\core\instance\init.js -> 初始化文件,主要关注initState()这个方法,其他的后面再做研究

export function initMixin(Vue: Class<Component>) {
  //在initMinxin里面定义Vue的原型方法_init
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this;
    // a uid
    vm._uid = uid++;

    let startTag, endTag;

    // a flag to avoid this being observed
    vm._isVue = true;
    //合并选项
    // merge options
    if (options && options._isComponent) {
      //判断是不是一个组件,是的话执行initInternalComponent,否则合并options
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options);
    } else {
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      );
    }
    /* istanbul ignore else */
    if (process.env.NODE_ENV !== "production") {
      initProxy(vm);
    } else {
      vm._renderProxy = vm;
    }
    // expose real self
    vm._self = vm; //把vm放在_self属性上暴露出去
    initLifecycle(vm); //初始化生命周期:$parent,$root,$children,$refs
    initEvents(vm); //对父组件传入事件添加监听,初始化事件
    initRender(vm); //生命$slots,$createElemnet,渲染相关的
    callHook(vm, "beforeCreate");
    initInjections(vm); // resolve injections before data/props,注入数据
    //数据初始化
    initState(vm); //初始化props、data、watch、methods、computed等属性,因此在beforeCreate的钩子函数中获取不到前面的这些定义的属性和方法
    initProvide(vm); // resolve provide after data/props,提供数据
    callHook(vm, "created");
    //最终挂载方法
    if (vm.$options.el) {
      vm.$mount(vm.$options.el);
    }
  };
}

3.src\core\instance\state.js

export function initState(vm: Component) {
  vm._watchers = [];
  const opts = vm.$options;
  //初始化props
  if (opts.props) initProps(vm, opts.props);
  //初始化methods
  if (opts.methods) initMethods(vm, opts.methods);
  //初始化data
  if (opts.data) {
    initData(vm);
  } else {
    //没有创建则创建一个空对象,并设置位响应式
    observe((vm._data = {}), true /* asRootData */);
  }
  //初始化computed
  if (opts.computed) initComputed(vm, opts.computed);
  //初始化watch
  if (opts.watch && opts.watch !== nativeWatch) {
    initWatch(vm, opts.watch);
  }
}

initData方法

可以看到我们平时常见的一些报错提示信息

function initData(vm: Component) {
  let data = vm.$options.data;
  data = vm._data = typeof data === "function" ? getData(data, vm) : data || {};
  //校验data必须是一个对象,其他的类似Array,Function等也算是对象类型,但是有着更精确的数据类型
  if (!isPlainObject(data)) {
    data = {};
    process.env.NODE_ENV !== "production" &&
      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
  //获取所有的data中的Key
  const keys = Object.keys(data);
  //获取配置对象上的props属性
  const props = vm.$options.props;
  //获取配置对象上的methods属性,所有的方法都在Methods对象中
  const methods = vm.$options.methods;
  let i = keys.length;
  while (i--) {
    const key = keys[i];
    if (process.env.NODE_ENV !== "production") {
      //校验methods->Key的唯一性
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        );
      }
    }
    //校验props->Key的唯一性
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== "production" &&
        warn(
          `The data property "${key}" is already declared as a prop. ` +
            `Use prop default value instead.`,
          vm
        );
    } else if (!isReserved(key)) {
 //校验key是否是使用$或者下划线进行定义的,因为vue中定义了很多的$data,$el等来直接操作vue对象。把所有的key都代理到vm的_data属性上面
      proxy(vm, `_data`, key);
        //此处的proxy代理和observe中的walk代理有什么区别?
        //proxy代理的是将自定义的data中的所有属性定义到_data上,而walk中的Object.defineProperty则是针对data的值
    }
  }
  // observe data
  observe(data, true /* asRootData */);
}

4.src\core\observer\index.js
关键一步:ob = new Observer(value),开始构建observe对象

export function observe(value: any, asRootData: ? boolean): Observer | void {
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  let ob: Observer | void
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else if (
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value)
  }
  if (asRootData && ob) {
    ob.vmCount++
  }
  return ob
}

src\core\observer\index.js
在vue2中不能直接处理数组的方法,需要重写数组的方法,怎样重写数组的方法?看这里->监听数组变化的方法 原理是将常用的操作数组的方法列下来,替换原来数组的方法,同时也方便扩展。

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that have this object as root $data

  constructor(value: any) {
    this.value = value
    //创建依赖收集的容器
    this.dep = new Dep()
    this.vmCount = 0
    //设置一个__ob__属性引用当前observer实例
    def(value, '__ob__', this)
    //判断类型
    if (Array.isArray(value)) {
      //如果是数组,替换数组对象的原型
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      //如果数组里面元素是对象还需要做响应化处理
      this.observeArray(value)
    } else {
      //walk方法,将每个值进行响应化处理
      this.walk(value)
    }
  }
  /**
   * Walk through all properties and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk(obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }
  /**
   * Observe a list of Array items.
   */
  observeArray(items: Array < any > ) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}

defineReactive()可以看到我们常说的Object.defineProperty了,至此响应化data的部分就已经完成了。

export function defineReactive(
  obj: Object,
  key: string,
  val: any,
  customSetter ? : ? Function,
  shallow ? : boolean
) {
  //和Key一一对应
  const dep = new Dep()
  //childOb,属性拦截,只要是对象类型都会返回childobj
  let childOb = !shallow && observe(val)
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      const value = getter ? getter.call(obj) : val
      //如果存在依赖
      if (Dep.target) {
        //依赖收集
        dep.depend()
        if (childOb) {
          //如果存在子obj,子obj也收集这个依赖?为什么要这么做?作用:Obj和父变了和子改变了都会通知进行更新。例:在访问时{obj.foo}无论是Obj变了还是obj.foo的值变了都会通知进行更新。
          childOb.dep.depend()
          if (Array.isArray(value)) {
            dependArray(value)
          }
        }
      }
      return value
    },
    set: function reactiveSetter(newVal) {
      const 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 (process.env.NODE_ENV !== 'production' && 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()
    }
  })
}

以上是我对vue初始化data的理解,有不对之处欢迎指证,共同学习!

上一篇 下一篇

猜你喜欢

热点阅读