Vue beforeCreate 到 created 之间用到的几个方法

730 阅读1分钟

initInjections(vm); // resolve injections before data/props

function initInjections (vm) {
    var result = resolveInject(vm.$options.inject, vm);  //返回 key value 对象
    if (result) {
      toggleObserving(false); 
      //关闭 observe ,不允许对 Inject 中的值赋值,所以 injections不需要observe
      Object.keys(result).forEach(function (key) {
        /* istanbul ignore else */
        {
          defineReactive$$1(vm, key, result[key], function () {
            warn(
              "Avoid mutating an injected value directly since the changes will be " +
              "overwritten whenever the provided component re-renders. " +
              "injection being mutated: \"" + key + "\"",
              vm
            );
          });
        }
      });
      toggleObserving(true); //开启 observe
    }
  }

resolveInject 方法主要是将provide和inject整合在一起,并返回最终的结果

for(var i = 0; i < keys.length; i++)
...
while (source) { //遍历vm,从当前vm开始,一层层往上,找到有‘key’的provide的值
    if (source._provided && hasOwn(source._provided, provideKey)) {
        result[key] = source._provided[provideKey];
        break //有值则跳出当前的for循环
    }
    source = source.$parent;
}

如果没有找到会用 injectdefault 方法

if (!source) {
    if ('default' in inject[key]) {
        var provideDefault = inject[key].default;
        result[key] = typeof provideDefault === 'function'
        ? provideDefault.call(vm)
        : provideDefault;
    } else {
    	warn(("Injection \"" + key + "\" not found"), vm);
    }
}

return result  //返回最终的结果

initState 初始化了5个属性

  function initState (vm) {
    vm._watchers = [];
    var opts = vm.$options;
    if (opts.props) { initProps(vm, opts.props); }
    if (opts.methods) { initMethods(vm, opts.methods); }
    if (opts.data) {
      initData(vm);
    } else {
      observe(vm._data = {}, true /* asRootData */);
    }
    if (opts.computed) { initComputed(vm, opts.computed); }
    if (opts.watch && opts.watch !== nativeWatch) {
      initWatch(vm, opts.watch);
    }
  }

initProps (和 inject 一样不允许修改,都是单向数据流)

	var loop = function ( key ) {
      keys.push(key);
      var value = validateProp(key, propsOptions, propsData, vm);
      /* istanbul ignore else */
      {
        var hyphenatedKey = hyphenate(key);
        if (isReservedAttribute(hyphenatedKey) ||
            config.isReservedAttr(hyphenatedKey)) {
          warn(
            ("\"" + hyphenatedKey + "\" is a reserved attribute and cannot be used as component prop."),
            vm
          );
        } // prop不能是保留的属性
        ......
      }
      // static props are already proxied on the component's prototype
      // during Vue.extend(). We only need to proxy props defined at
      // instantiation here.
      if (!(key in vm)) {
        proxy(vm, "_props", key); //proxy代理props
      }
    };

initMethods

vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);
//methods用bind方法指向this

var bind = Function.prototype.bind
  ? nativeBind
  : polyfillBind;

function polyfillBind (fn, ctx) {
  function boundFn (a) {
    var l = arguments.length;
    return l
      ? l > 1
        ? fn.apply(ctx, arguments)
        : fn.call(ctx, a)
      : fn.call(ctx)
  }

  boundFn._length = fn.length;
  return boundFn
}
function nativeBind (fn, ctx) {
  return fn.bind(ctx)
}

initData

首先会判断data是函数还是对象的写法然后取值

data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {};

这里会判断 data 不能和 props、methods 中的 key 重名

  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 (process.env.NODE_ENV !== 'production') {
      if (methods && hasOwn(methods, key)) {
        warn(
          ("Method \"" + key + "\" has already been defined as a data property."),
          vm
        );
      }
    }
    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)) {
      proxy(vm, "_data", key);
    }
  }

判断没有重名之后开始 observe data

...
observe(data, true /* asRootData */);
...
function observe (value, asRootData) {
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  var ob;
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__; //判断是否有__ob__属性并且__ob__是Observer函数的实例
  } else if (
    shouldObserve && //开关
    !isServerRendering() && // 服务端渲染
    (Array.isArray(value) || isPlainObject(value)) && // 数组或对象(不是null)
    Object.isExtensible(value) && //value可扩展
    !value._isVue //没有_isVue属性
  ) {
    ob = new Observer(value); //调用Observer方法
  }
  if (asRootData && ob) {
    ob.vmCount++;
  }
  return ob
}

Observer 方法

var Observer = function Observer (value) {
  this.value = value;
  this.dep = new Dep();
  this.vmCount = 0;
  def(value, '__ob__', this); //调用def函数给value增加__ob__属性
  if (Array.isArray(value)) { //数组
  	//两个方法都是重新定义数组的,对value数组的方法增加劫持
    if (hasProto) {
    	//	如果浏览器支持__proto__属性的,将value的__proto__指向arrayMethods
      protoAugment(value, arrayMethods);
    } else {
    	//不支持的话调用def函数将arrayMethods中的几个方法替换value原生的方法
      copyAugment(value, arrayMethods, arrayKeys);
    }
    this.observeArray(value); //observe数组中的每一项
  } else { //对象
    this.walk(value); //对value对象中的每个可枚举key进行双向绑定,walk方法中用到了defineReactive$$1
  }
};

protoAugmentcopyAugment

/**
 * Augment a target Object or Array by intercepting
 * the prototype chain using __proto__
 */
function protoAugment (target, src) {
  /* eslint-disable no-proto */
  target.__proto__ = src;
  /* eslint-enable no-proto */
}

/**
 * Augment a target Object or Array by defining
 * hidden properties.
 */
/* istanbul ignore next */
function copyAugment (target, src, keys) {
  for (var i = 0, l = keys.length; i < l; i++) {
    var key = keys[i];
    def(target, key, src[key]);
  }
}

defineReactive?1函数

function defineReactive$$1 (
  obj,
  key,
  val,
  customSetter,
  shallow
) {
  var dep = new Dep();

  var property = Object.getOwnPropertyDescriptor(obj, key);
  if (property && property.configurable === false) {
  	//Object.freeze(obj) 可以去除双向绑定
    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];
  } // 对property进行取值(如果没有设置了get,set方法 Object.defineProperty...)

  var childOb = !shallow && observe(val);
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
      var value = getter ? getter.call(obj) : val; //取值的时候进行判断
      if (Dep.target) { //在Watcher的get方法中会给Dep.target赋值,是一个Watcher函数的实例
        dep.depend(); // Watcher实例收集依赖
        if (childOb) {  //如果有子对象
          childOb.dep.depend(); // watcher实例收集子对象依赖
          if (Array.isArray(value)) { //判断是否为数组
            dependArray(value); // watcher实例收集数组中的每个元素
          }
        }
      }
      return value
    },
    set: function reactiveSetter (newVal) {
      var value = getter ? getter.call(obj) : val;
      if (newVal === value || (newVal !== newVal && value !== value)) {
        return
      }
      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(); //触发notify 视图更新
    }
  });
}

initComputed

生成一个computed 的watcher(观察者)

...
if (!isSSR) {
        // create internal watcher for the computed property.
        watchers[key] = new Watcher(
          vm,
          getter || noop,
          noop,
          computedWatcherOptions
        );
      }
...
defineComputed(vm, key, userDef);
//这里用Object.defineProperty去定义computed上的key

initWatch

watch对象里面的方法都是用 $watch 方法来初始化的

Vue.prototype.$watch = function (
      expOrFn,
      cb,
      options
    ) {
      ...
      var watcher = new Watcher(vm, expOrFn, cb, options); //生成对应的watcher
      if (options.immediate) {
        try {
          cb.call(vm, watcher.value);
        } catch (error) {
          handleError(error, vm, ("callback for immediate watcher \"" + (watcher.expression) + "\""));
        }
      }
      return function unwatchFn () { //返回一个可以取消观察的函数
        watcher.teardown();
      }
    };

initProvide

function initProvide (vm) {
  var provide = vm.$options.provide;
  if (provide) {
    vm._provided = typeof provide === 'function' //判断是函数还是对象,取值
      ? provide.call(vm)
      : provide;
  }
}

beforeCreatecreated 主要是三个方法: initInjections, initState, initProvide, 并且inject必须在data之前,因为data中可以访问到this.[inject中的属性], provide可以访问data中的值,所以要在data之后初始化