前言

本文是vue2.x源码分析的第四篇,主要讲解vue实例的初始化过程init*系列!

先看调用形式

  1. initLifecycle(vm);
  2. initEvents(vm);
  3. initRender(vm);
  4. callHook(vm, 'beforeCreate');
  5. initInjections(vm); // 在data/props之前处理注入,暂不清楚作用Unknown4.1,本节不分析
  6. initState(vm); //最主要的函数
  7. initProvide(vm); // 在data/props之后处理provide,暂不清楚作用Unknown4.2,本节不分析
  8. callHook(vm, 'created');

1、分析initLifecycle(vm),initEvents(vm),initRender(vm)

1.1、initLifecycle(vm)

  1. function initLifecycle (vm) {
  2. var options = vm.$options;
  3. // 定位第一个非抽象的parent,记为Unknown4.1
  4. var parent = options.parent;
  5. if (parent && !options.abstract) {
  6. while (parent.$options.abstract && parent.$parent) {
  7. parent = parent.$parent;
  8. }
  9. parent.$children.push(vm);
  10. }
  11. //给vm实例添加如下属性
  12. vm.$parent = parent;
  13. vm.$root = parent ? parent.$root : vm;
  14. vm.$children = [];
  15. vm.$refs = {};
  16. vm._watcher = null;
  17. vm._inactive = null;
  18. vm._directInactive = false;
  19. vm._isMounted = false;
  20. vm._isDestroyed = false;
  21. vm._isBeingDestroyed = false;
  22. }

1.2、initEvents(vm)

  1. function initEvents (vm) {
  2. vm._events = Object.create(null);
  3. vm._hasHookEvent = false;
  4. // 处理父元素中的events,Unknown4.2
  5. var listeners = vm.$options._parentListeners;
  6. if (listeners) {
  7. updateComponentListeners(vm, listeners);
  8. }
  9. }

1.3、initRender(vm)

  1. function initRender (vm) {
  2. vm.$vnode = null; // the placeholder node in parent tree,在父树中的位置
  3. vm._vnode = null; // the root of the child tree,子树的根
  4. vm._staticTrees = null;//静态树
  5. var parentVnode = vm.$options._parentVnode;
  6. var renderContext = parentVnode && parentVnode.context;
  7. vm.$slots = resolveSlots(vm.$options._renderChildren, renderContext);//处理slot
  8. vm.$scopedSlots = emptyObject;
  9. // 将createElement函数绑定到该实例,参数顺序:tag, data, children, normalizationType, alwaysNormalize.
  10. vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };//内部调用版
  11. vm.$createElement = function (a, b, c, d) { return createElement(vm, a, b, c, d, true); };外部调用版
  12. }

之后,beforeCreate生命周期函数被调用:callHook(vm, ‘beforeCreate’);

2、initState(vm)

  1. function initState (vm) {
  2. vm._watchers = []; //用于存放所用的watcher实例
  3. var opts = vm.$options;
  4. if (opts.props) {
  5. initProps(vm, opts.props); //对props各项进行验证
  6. }
  7. if (opts.methods) {
  8. initMethods(vm, opts.methods); //将methods中各项添加到vm上
  9. }
  10. if (opts.data) {
  11. initData(vm); //对data进行观测
  12. } else {
  13. observe(vm._data = {}, true /* asRootData */);
  14. }
  15. if (opts.computed) {
  16. initComputed(vm, opts.computed); //将computed属性添加到vm,并定义响应式
  17. }
  18. if (opts.watch) {
  19. initWatch(vm, opts.watch); //对watch属性进行处理...
  20. }
  21. }

2.1、initProps(vm,propsOptions)

  1. function initProps (vm, propsOptions) {
  2. var propsData = vm.$options.propsData || {};
  3. var props = vm._props = {};
  4. // 对props中key进行缓存以减少遍历
  5. var keys = vm.$options._propKeys = [];
  6. var isRoot = !vm.$parent; //是否是根节点
  7. // 根实例的props需要被转换
  8. observerState.shouldConvert = isRoot; //observeState={isSettingProps:false,shouldConvert:false}
  9. var loop = function ( key ) {
  10. keys.push(key);
  11. var value = validateProp(key, propsOptions, propsData, vm);
  12. {
  13. if (isReservedProp[key]) { //判断是否是保留的prop(不能是key,slot,ref)
  14. warn(
  15. ("\"" + key + "\" is a reserved attribute and cannot be used as component prop."),
  16. vm
  17. );
  18. }
  19. //将props的key定义为响应式,这里的匿名函数是作为customSetter用的,defineReactive$$1函数后面会分析
  20. defineReactive$$1(props, key, value, function () {
  21. if (vm.$parent && !observerState.isSettingProps) {
  22. warn(
  23. "Avoid mutating a prop directly since the value will be " +
  24. "overwritten whenever the parent component re-renders. " +
  25. "Instead, use a data or computed property based on the prop's " +
  26. "value. Prop being mutated: \"" + key + "\"",
  27. vm
  28. );
  29. }
  30. });
  31. }
  32. /* 静态props在Vue.extend()时已经被代理,这里只需对实例上的props进行代理,代理的目的很简单,
  33. 就是使props中的属性可以用vm.xxx访问,而不必vm.props.xxx,实现很简单,就是使用API:Object.defineProperty*/
  34. if (!(key in vm)) {
  35. proxy(vm, "_props", key);
  36. }
  37. };
  38. for (var key in propsOptions) loop( key );
  39. observerState.shouldConvert = true;
  40. }

主要分析下validateProp(key, propsOptions, propsData, vm)

  1. //该函数就是对prop进行验证,如type、default、required、validator
  2. function validateProp (
  3. key,
  4. propOptions,
  5. propsData,
  6. vm
  7. ) {
  8. var prop = propOptions[key];
  9. var absent = !hasOwn(propsData, key);
  10. var value = propsData[key];
  11. // 处理布尔类型的prop
  12. if (isType(Boolean, prop.type)) {
  13. if (absent && !hasOwn(prop, 'default')) {
  14. value = false;
  15. } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) {
  16. value = true;
  17. }
  18. }
  19. if (value === undefined) {
  20. value = getPropDefaultValue(vm, prop, key);//当type为object时,default必须是函数
  21. // 观测value
  22. var prevShouldConvert = observerState.shouldConvert;
  23. observerState.shouldConvert = true;
  24. observe(value);
  25. observerState.shouldConvert = prevShouldConvert;
  26. }
  27. {
  28. assertProp(prop, key, value, vm, absent);//检查是否有required和validator
  29. }
  30. return value
  31. }

2.2、initMethods(vm, opts.methods)

  1. function initMethods (vm, methods) {
  2. var props = vm.$options.props;
  3. for (var key in methods) {
  4. //遍历methods,将methods中的方法挂在vm实例上,注意这里调用了bind函数,故methods中所有方法的this都是vm对象
  5. vm[key] = methods[key] == null ? noop : bind(methods[key], vm);
  6. {
  7. if (methods[key] == null) {
  8. warn(
  9. "method \"" + key + "\" has an undefined value in the component definition. " +
  10. "Did you reference the function correctly?",
  11. vm
  12. );
  13. }
  14. //检测props中是否与methods中有同名属性
  15. if (props && hasOwn(props, key)) {
  16. warn(
  17. ("method \"" + key + "\" has already been defined as a prop."),
  18. vm
  19. );
  20. }
  21. }
  22. }
  23. }

2.3、initData(vm)

  1. function initData (vm) {
  2. //这里的data通过策略合并对象变成了函数mergedInstanceDataFn
  3. var data = vm.$options.data;
  4. data = vm._data = typeof data === 'function'
  5. ? getData(data, vm) //getData就是执行mergedInstanceDataFn函数,返回data对象
  6. : data || {};
  7. //当data函数返回的不是对象时
  8. if (!isPlainObject(data)) {
  9. data = {};
  10. "development" !== 'production' && warn(
  11. 'data functions should return an object:\n' +
  12. 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
  13. vm
  14. );
  15. }
  16. // 在实例vm上代理data中各项,作用同props的代理
  17. var keys = Object.keys(data);
  18. var props = vm.$options.props;
  19. var i = keys.length;
  20. while (i--) {
  21. //data中属性不能和props中同名
  22. if (props && hasOwn(props, keys[i])) {
  23. "development" !== 'production' && warn(
  24. "The data property \"" + (keys[i]) + "\" is already declared as a prop. " +
  25. "Use prop default value instead.",
  26. vm
  27. );
  28. } else if (!isReserved(keys[i])) { //data中属性不能以_或$开头
  29. proxy(vm, "_data", keys[i]);
  30. }
  31. }
  32. // 对data进行观测
  33. observe(data, true /* asRootData */);
  34. }

这里主要分析observe(data, true)

  1. //给value添加一个observer,保存在value.__ob__属性上。
  2. function observe (value, asRootData) {
  3. //不对普通类型观测
  4. if (!isObject(value)) {
  5. return
  6. }
  7. var ob;
  8. if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
  9. ob = value.__ob__;
  10. } else if (
  11. observerState.shouldConvert &&
  12. !isServerRendering() &&
  13. (Array.isArray(value) || isPlainObject(value)) &&
  14. Object.isExtensible(value) &&
  15. !value._isVue //当有_isVue属性时,该value不会被观测
  16. ) {
  17. ob = new Observer(value); //主要函数,实例化一个Observer
  18. }
  19. if (asRootData && ob) {
  20. ob.vmCount++;
  21. }
  22. return ob
  23. }

来看看Observer(value)

  1. var Observer = function Observer (value) {
  2. this.value = value;
  3. this.dep = new Dep(); //每个observer都实例化一个Dep,用于收集依赖
  4. this.vmCount = 0;
  5. def(value, '__ob__', this); //将__ob__放在value上,值为observer对象
  6. if (Array.isArray(value)) { //当value是数组
  7. var augment = hasProto
  8. ? protoAugment
  9. : copyAugment;
  10. augment(value, arrayMethods, arrayKeys);
  11. this.observeArray(value);
  12. } else {
  13. this.walk(value); //当value是对象
  14. }
  15. };

来看看walk(value)

  1. Observer.prototype.walk = function walk (obj) {
  2. var keys = Object.keys(obj); //这里__ob__不会出现在keys里
  3. for (var i = 0; i < keys.length; i++) {
  4. defineReactive$$1(obj, keys[i], obj[keys[i]]); //对obj中每一项进行响应式定义
  5. }
  6. };

来看看defineReactive$$1(obj, keys[i], obj[keys[i]])

  1. function defineReactive$$1 (obj,key,val,customSetter) {
  2. var dep = new Dep();
  3. var property = Object.getOwnPropertyDescriptor(obj, key);
  4. if (property && property.configurable === false) {
  5. return
  6. }
  7. // 引用预先定义的getter/setters
  8. var getter = property && property.get;
  9. var setter = property && property.set;
  10. //val可能是对象,故继续观测
  11. var childOb = observe(val);
  12. Object.defineProperty(obj, key, {
  13. enumerable: true,
  14. configurable: true,
  15. get: function reactiveGetter () {
  16. var value = getter ? getter.call(obj) : val;
  17. if (Dep.target) {
  18. dep.depend();
  19. //子对象也收集父对象的依赖
  20. if (childOb) {
  21. childOb.dep.depend();
  22. }
  23. //对数组的依赖处理
  24. if (Array.isArray(value)) {
  25. dependArray(value);
  26. }
  27. }
  28. return value
  29. },
  30. set: function reactiveSetter (newVal) {
  31. var value = getter ? getter.call(obj) : val;
  32. if (newVal === value || (newVal !== newVal && value !== value)) {
  33. return
  34. }
  35. if ("development" !== 'production' && customSetter) {
  36. customSetter();//报错用
  37. }
  38. if (setter) {
  39. setter.call(obj, newVal);
  40. } else {
  41. val = newVal;
  42. }
  43. childOb = observe(newVal); //设置新值后,对新值进行观测
  44. dep.notify(); //触发观测器的回调或get函数
  45. }
  46. });
  47. }

2.4、initComputed(vm, opts.computed)

  1. function initComputed (vm, computed) {
  2. var watchers = vm._computedWatchers = Object.create(null);//computed中watcher存放在vm._computedWatchers
  3. for (var key in computed) {
  4. var userDef = computed[key];
  5. var getter = typeof userDef === 'function' ? userDef : userDef.get;
  6. {
  7. if (getter === undefined) {
  8. warn(
  9. ("No getter function has been defined for computed property \"" + key + "\"."),
  10. vm
  11. );
  12. getter = noop;
  13. }
  14. }
  15. // 对computed中每一个属性创建Watcher.
  16. watchers[key] = new Watcher(vm, getter, noop, computedWatcherOptions);
  17. // 将computed中的属性定义到vm上,跟代理差不多,使vm.xx等价于vm.computed.xx
  18. if (!(key in vm)) { //computed中属性若和vm上同名会被vm上的覆盖
  19. defineComputed(vm, key, userDef);
  20. }
  21. }
  22. }

来看看Watcher(vm, getter, noop, computedWatcherOptions)

  1. var Watcher = function Watcher (vm,expOrFn,cb,options) {
  2. this.vm = vm;
  3. vm._watchers.push(this); //vm._watchers存放所有的watcher实例
  4. // 处理options,在initComputed中,options={lazy:true},即lazy watchers;
  5. // 在initWatch中,options={user:true}
  6. if (options) {
  7. this.deep = !!options.deep;
  8. this.user = !!options.user;
  9. this.lazy = !!options.lazy;
  10. this.sync = !!options.sync;
  11. } else {
  12. this.deep = this.user = this.lazy = this.sync = false;
  13. }
  14. this.cb = cb; //存放回调函数cb
  15. this.id = ++uid$2; // uid for batching
  16. this.active = true;
  17. this.dirty = this.lazy; // for lazy watchers
  18. this.deps = [];
  19. this.newDeps = [];
  20. this.depIds = new _Set();
  21. this.newDepIds = new _Set();
  22. this.expression = expOrFn.toString(); //存放传入的expOrFn
  23. if (typeof expOrFn === 'function') {
  24. // 当expOrFn是函数,initComputed从这儿走
  25. this.getter = expOrFn;
  26. } else {
  27. // 当expOrFn是字符串,initWatch从这儿走
  28. this.getter = parsePath(expOrFn);//字符串若有分隔,只能用'.'号,不能用空格,'-'等
  29. if (!this.getter) {
  30. this.getter = function () {};
  31. "development" !== 'production' && warn(
  32. "Failed watching path: \"" + expOrFn + "\" " +
  33. 'Watcher only accepts simple dot-delimited paths. ' +
  34. 'For full control, use a function instead.',
  35. vm
  36. );
  37. }
  38. }
  39. this.value = this.lazy //initComputed中lazy是true,故不调用get函数;initWatcher中会调用get
  40. ? undefined
  41. : this.get();
  42. };

2.4、initWatch(vm, opts.watch)

  1. function initWatch (vm, watch) {
  2. for (var key in watch) {
  3. var handler = watch[key];
  4. if (Array.isArray(handler)) { //当handler是数组时,分别调用createWatcher
  5. for (var i = 0; i < handler.length; i++) {
  6. createWatcher(vm, key, handler[i]);
  7. }
  8. } else {
  9. createWatcher(vm, key, handler);
  10. }
  11. }
  12. }

来看看createWatcher(vm, key, handler)

  1. function createWatcher (vm, key, handler) {
  2. var options;
  3. if (isPlainObject(handler)) { //当handler是数组时应该提供handler属性
  4. options = handler;
  5. handler = handler.handler;
  6. }
  7. if (typeof handler === 'string') { //当handler是字符串,从vm上找同名属性
  8. handler = vm[handler];
  9. }
  10. vm.$watch(key, handler, options); //主要函数
  11. }

来看看vm.$watch(key,handler,options)

  1. Vue.prototype.$watch = function (expOrFn,cb,options) {
  2. var vm = this;
  3. options = options || {};
  4. options.user = true; //user表示是否是用户自定义的watcher
  5. var watcher = new Watcher(vm, expOrFn, cb, options); //回到2.3中再看一遍,会发现最后调用get函数
  6. if (options.immediate) {
  7. cb.call(vm, watcher.value);
  8. }
  9. return function unwatchFn () { //返回解除watcher的函数
  10. watcher.teardown();
  11. }
  12. };

来看看get函数

  1. Watcher.prototype.get = function get () {
  2. pushTarget(this);//内部执行了Dep.target = this,并将this存入栈targetStack;
  3. var value;
  4. var vm = this.vm;
  5. if (this.user) {
  6. try {
  7. value = this.getter.call(vm, vm);//调用getter,在initWatch中即调用如下函数:
  8. // function (obj) { //这里的obj=vm
  9. // for (var i = 0; i < segments.length; i++) { //segments是对观测表达式进行的分割
  10. // if (!obj) { return }
  11. // obj = obj[segments[i]]; //当segments.length>1时,第一次从vm上找同名属性返回,后面就从obj上找,例如
  12. // //当观测表达式是'article.title',那么segments=['article','title']
  13. // //先找vm.article,再在article上找title作为结果返回
  14. // }
  15. // return obj
  16. // }
  17. } catch (e) {
  18. handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
  19. }
  20. } else {
  21. value = this.getter.call(vm, vm);
  22. }
  23. // "touch" every property so they are all tracked as
  24. // dependencies for deep watching
  25. if (this.deep) { //是否进行深度观测
  26. traverse(value);
  27. }
  28. popTarget(); //从targetStack栈中弹出this,将Deep.target仍为this
  29. this.cleanupDeps(); //暂不清楚,Unknown4.1
  30. return value
  31. };

当initState调用完成后,created生命周期函数被调用:callHook(vm, ‘created’);

3、小结

  由于还没到渲染环节,故本节中定义的响应式属性还不能起作用,不太好理解,下一节将会看到
这些响应式属性是怎么起作用的