vue-生命周期

191 阅读2分钟

vue的生命周期包含从创建-->挂载渲染-->更新渲染-->销毁的过程,生命周期钩子可以让我们更好的控制vue实例更新渲染的过程。

生命周期

创建beforeCreate|created

  • beforeCreate:数据等初始化之前被调用
  • created:数据等初始化后被调用。此时,已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。

挂载beforeMount|mounted

  • beforeMount:此时,传递的el|template已经被编译为render函数,render函数的优先级最高
  • mounted:render触发,新创建的$el替换原来的el

更新beforeUpdate|updated

  • beforeUpdate:数据变化后,虚拟dom更新前调用,可以用于获取数据更新前的dom
  • updated:re-render,可在此执行依赖于dom的操作;但是此时,并非所有dom都会重绘,可以使用$nextTick

销毁beforeDestroy|destroyed

  • beforeDestroy: 实例销毁前调用,可以移除事件监听器、定时器等,避免内存泄漏
  • destroyed:实例销毁后调用,所有的子实例都销毁,所有事件被移除

钩子实现

它的实现主要有三个问题: 1. 同类钩子如何合并? 2. 钩子怎么触发? 3. 钩子什么时候触发?

数据合并

对于普通数据parent和child,分别遍历parent和child,执行属性合并

普通合并策略

如果是对象,可以直接合并;如果child对应的属性不存在,就是用parent的属性值;否则,全部是用child的属性值

function mergeOptions(parent, child) {
  const options = {};
  for(let key in parent) {  mergeField(key); }
  for(let key in child) {
    if (!parent.hasOwnProperty(key)){ mergeField(key); }
  }
  function mergeField(key) {
    if (strats[key]) { // 如生命周期的合并
      return options[key] = strats[key](parent[key], child[key])
    }
    if(typeof parent[key] === 'object' && typeof child[key] === 'object') {
      options[key] = {
        ...parent[key],
        ...child[key]
      }
    } else if(child[key] == null){
      options[key] = parent[key]
    } else {
      options[key] = child[key]
    }
  }
  return options
}
特定合并策略

特定合并策略,比如针对生命周期的合并,定义单独的合并策略

const LISECYCLE_HOOKS=['beforeCreate',  'created', 'beforeMount', 'mounted', 'beforeUpdate', 'updated', 'beforeDestroy', 'destroyed']
function mergeHook(parentVal, childVal) {
  if (childVal) {
    if (parentVal) {
      return parentVal.concat(childVal)
    } else {
      return [childVal]
    }
  } else {
    return parentVal
  }
}
let strats = {};
LISECYCLE_HOOKS.forEach(hook => {
  strats[hook] = mergeHook;
})

同类钩子合并

所有同类钩子是存放在数组中的,当需要触发时,会遍历执行;它的合并主要涉及全局设置钩子和组件自定义钩子合并

全局设置钩子合并

在初始化全局api的时候,在Vue上定义options用于保存全局相关内容;另外,会定义Vue.mixin方法,用于合并directives|filters|components等

function initGlobalAPI(Vue){
  Vue.options = {};
  Vue.mixin = function (mixin) {
    this.options = mergeOptions(this.options, mixin)
  }
}
组件自定义钩子合并

自定义钩子合并,在实例初始化时,将用户传递的options和Vue.options合并绑定在vm.$options上时,钩子也默认被合并

Vue.prototype._init = function(opts) {
    const vm = this;
    vm.$options = mergeOptions(vm.constructor.options, opts);
    ...
}

钩子触发

钩子触发,从vm.$options上找到指定的钩子数组,遍历执行

function callHook(vm, hook) {
  const handlers = vm.$options[hook];
  if (handlers) {
    for (let i = 0; i < handlers.length; i++) {
      const curHook = handlers[i];
      curHook.call(vm)
      
    }
  }
}

钩子触发时机

从上述生命周期中可以看到,在数据初始化前后会触发beforeCreated和creatd,而在组件加载前后触发beforeMount和mounted

Vue.prototype._init = function(opts) {
    ....
    callHook(vm, 'beforeCreate');
    initState(vm);
    callHook(vm, 'created');
    ...
}
function mountComponent(vm, el){
  ...
  callHook(vm, 'beforeMount');
  let updateComponent= () => {
    vm._update(vm._render());
  }
  new Watcher(vm, updateComponent, () => {}, true);
  callHook(vm, 'mounted');
}