mounted 为什么能够访问到 dom 如果是组件,是在 vnode patch 的 insert 钩子函数中执行,insert 是先子后父,所以此时的 mounted 执行实际也是先子后父 如果不是组件,vnode patch 了,最终成了真正的 dom,才会执行 两者的相同点是 vm._render()之后,vm._render()已经把 vnode 渲染成真实的 dom 了 生命周期的定义…

生命周期

mounted 为什么能够访问到 dom

如果是组件,是在 vnode patch 的 insert 钩子函数中执行,insert 是先子后父,所以此时的 mounted 执行实际也是先子后父

如果不是组件,vnode patch 了,最终成了真正的 dom,才会执行

两者的相同点是 vm._render()之后,vm._render()已经把 vnode 渲染成真实的 dom 了

生命周期的定义

每一个 Vue 实例,在创建前都要进行数据监听,编译模板,挂载实例到 DOM,监听数据变化后更新 DOM 等过程,此过程中,Vue 会在不同时期暴露不同的 API,用于开发者在想要的时期添加自己的代码

  1. 1.lifecycle
// src/core/instance/lifecycle
// 根据传入的字符串hook,去拿到vm.$options[hook]对应的回调函数数组,遍历数组的同时把vm作为上下文
// 在vue合并的过程中,会把生命周期函数也合并到vm.$options中
// callHook的功能就是调用某个生命周期注册的所有钩子函数
export function callHook(vm: Component, hook: string) {
  pushTarget();
  const handlers = vm.$options[hook];
  if (handlers) {
    for (let i = 0, j = handlers.length; i < j; i++) {
      try {
        handlers[i].call(vm);
      } catch (e) {
        handleError(e, vm, `${hook} hook`);
      }
    }
  }
  if (vm._hasHookEvent) {
    vm.$emit('hook:' + hook);
  }
  popTarget();
}
  1. 2.beforeCreate & created
// src/core/instance/init.js
// beforeCreate & created都是在实例化Vue阶段执行的
// foreCreate不能获取到props,data,methods,watch,computed这些字段
// 而create可以获取到
Vue.prototype._init = function(options?: Object) {
  // ...
  initLifecycle(vm);
  initEvents(vm);
  initRender(vm);
  callHook(vm, 'beforeCreate');
  initInjections(vm); // resolve injections before data/props
  // initState是初始化props,data,methods,watch,computed等属性
  // 所以beforeCreate不能获取到这些字段,created可以获取到
  initState(vm);
  initProvide(vm); // resolve provide after data/props
  callHook(vm, 'created');
  // ...
};
  1. 3.beforeMount & mounted
// src/core/instance/lifecycle.js
// beforeMount在挂载DOM之前执行的
// mounted根据是组件,如果是组件,是在vnode patch的 insert钩子函数中执行,insert是先子后父,所以此时的mounted也是先子后父
// mounted不是组件,vnode patch了,最终成了真正的dom,才会执行
export function mountComponent(vm: Component, el: ?Element, hydrating?: boolean): Component {
  vm.$el = el;
  if (!vm.$options.render) {
    vm.$options.render = createEmptyVNode;
    if (process.env.NODE_ENV !== 'production') {
      /* istanbul ignore if */
      if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') || vm.$options.el || el) {
        warn('You are using the runtime-only build of Vue where the template ' + 'compiler is not available. Either pre-compile the templates into ' + 'render functions, or use the compiler-included build.', vm);
      } else {
        warn('Failed to mount component: template or render function not defined.', vm);
      }
    }
  }
  // 在执行 vm._render() 函数渲染 VNode 之前,执行了beforeMount钩子函数
  callHook(vm, 'beforeMount');

  let updateComponent;
  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    updateComponent = () => {
      const name = vm._name;
      const id = vm._uid;
      const startTag = `vue-perf-start:${id}`;
      const endTag = `vue-perf-end:${id}`;

      mark(startTag);
      const vnode = vm._render();
      mark(endTag);
      measure(`vue ${name} render`, startTag, endTag);

      mark(startTag);
      vm._update(vnode, hydrating);
      mark(endTag);
      measure(`vue ${name} patch`, startTag, endTag);
    };
  } else {
    updateComponent = () => {
      vm._update(vm._render(), hydrating);
    };
  }

  // 真正的组件mounted是在vnode patch过程中的insert钩子函数中
  // insert的过程是先子后父
  // 所以组件mounted也是先子后父
  new Watcher(
    vm,
    updateComponent,
    noop,
    {
      before() {
        if (vm._isMounted) {
          callHook(vm, 'beforeUpdate');
        }
      }
    },
    true /* isRenderWatcher */
  );
  hydrating = false;

  // 执行完vm._update()把Vnode patch成真实的dom后,执行mounted钩子
  // 如果vm.$vnode为null,表示不是一个组件初始化的过程,是一个外部使用new Vue的过程
  // 以下过程是外部new Vue初始化的mounted
  if (vm.$vnode == null) {
    vm._isMounted = true;

    callHook(vm, 'mounted');
  }
  return vm;
}
  1. 4.beforeUpdate & updated
// src/core/instance/lifecycle.js
// beforeUpdate是在Watcher的before函数中,而且必须是组件已经mounted之后,才能调用beforeUpdate钩子函数
// 数据更新就涉及Vue的双向绑定
export function mountComponent(vm: Component, el: ?Element, hydrating?: boolean): Component {
  // ...
  new Watcher(
    vm,
    updateComponent,
    noop,
    {
      before() {
        if (vm._isMounted) {
          // 可以看出,beforeUpdate是在Watcher的before函数中
          // 而且必须是组件已经mounted之后,才能调用beforeUpdate钩子函数
          callHook(vm, 'beforeUpdate');
        }
      }
    },
    true /* isRenderWatcher */
  );
  // ...
}
// src/core/observer/scheduler.js
// update是在更新了watcher数组后执行,必须满足当前watcher为vm._watcher 并且组件已经mounted
// mounted时会实例渲染一个watcher去监听vm的数据变化,如果数据变了,就会重新渲染
// 在mounted 实例化watcher的时候,isRenderWatcher为true,把watcher实例赋给vm,_watcher
// 在mounted实例化watcher的时候,isRenderWatcher为true,也会把watcher push 到vm._watchers
// vm._watcher是为了监听vm上的数据变化,是一个渲染相关的watcher,是一个数组
function flushSchedulerQueue() {
  // ...
  // 获取到 updatedQueue
  callUpdatedHooks(updatedQueue);
}

function callUpdatedHooks(queue) {
  let i = queue.length;
  while (i--) {
    const watcher = queue[i];
    const vm = watcher.vm;
    if (vm._watcher === watcher && vm._isMounted) {
      callHook(vm, 'updated');
    }
  }
}
// 这个就是isRenderWatcher为true的操作
export default class Watcher {
  // ...
  constructor(vm: Component, expOrFn: string | Function, cb: Function, options?: ?Object, isRenderWatcher?: boolean) {
    this.vm = vm;
    if (isRenderWatcher) {
      // 把watcher实例赋给vm,_watcher
      vm._watcher = this;
    }
    // push到vm._watchers
    vm._watchers.push(this);
    // ...
  }
}
  1. 5.beforeDestroy & destroyed
// src/core/instance/lifecycle.js
// 销毁动作有:从parent的$children删除本身,删除watcher,当前渲染的vnode执行销毁钩子函数
// beforeDestroy执行在组件执行销毁动作之前
// destroyed,销毁动作执行完,执行顺序先子后父
Vue.prototype.$destroy = function() {
  const vm: Component = this;
  if (vm._isBeingDestroyed) {
    return;
  }
  callHook(vm, 'beforeDestroy');
  vm._isBeingDestroyed = true;
  // remove self from parent
  const parent = vm.$parent;
  if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
    remove(parent.$children, vm);
  }
  // teardown watchers
  if (vm._watcher) {
    vm._watcher.teardown();
  }
  let i = vm._watchers.length;
  while (i--) {
    vm._watchers[i].teardown();
  }
  // remove reference from data ob
  // frozen object may not have observer.
  if (vm._data.__ob__) {
    vm._data.__ob__.vmCount--;
  }
  // call the last hook...
  vm._isDestroyed = true;
  //  触发它子组件的销毁钩子函数
  // 所以destroyed也是先子后父
  vm.__patch__(vm._vnode, null);
  // fire destroyed hook
  callHook(vm, 'destroyed');
  // turn off all instance listeners.
  vm.$off();
  // remove __vue__ reference
  if (vm.$el) {
    vm.$el.__vue__ = null;
  }
  // release circular reference (#6759)
  if (vm.$vnode) {
    vm.$vnode.parent = null;
  }
};

暂无评论