Skip to content

Vue 生命周期详解 #2

@xushaocong

Description

@xushaocong

1. Vue 实例化过程

结合源码,我们看下通过 new Vue() 实例化的过程

Vue.prototype._init = function (options?: Object) {
  // ... 以上代码省略
  initLifecycle(vm)
  initEvents(vm)
  initRender(vm)
  callHook(vm, 'beforeCreate')
  initInjections(vm) // resolve injections before data/props
  initState(vm)
  initProvide(vm) // resolve provide after data/props
  callHook(vm, 'created')

  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    vm._name = formatComponentName(vm, false)
    mark(endTag)
    measure(`vue ${vm._name} init`, startTag, endTag)
  }

  if (vm.$options.el) {
    vm.$mount(vm.$options.el)
  }
}

Vue 初始化过程顺序如下:

  • 初始化生命周期、初始化事件、初始化渲染
  • 抛出 beforeCreate 钩子
  • 初始化 injections、state、provide。其中初始化 State 包括了 props、data、methods、computed 和 watch
  • 抛出 created 钩子
  • 判断实例是否有 el 属性,有则调用 vm.$mount 方法挂载 vm

2. 生命周期钩子

2.1 生命周期钩子函数

mounted: function() {}
// 或者
mounted() {}

// ------分割线------

// 错误写法
mounted: () => {}

Vue 的生命周期函数是自动绑定 this 到实例上,使用箭头函数会出现 this 指向父级作用域,会报错

2.2 生命周期钩子详解

接下来会通过在每个生命周期钩子中打印某些信息,来得知当前钩子节点做了什么事情

1. beforeCreate

image

在 Vue 初始化实例方法调用之后,抛出 beforeCreate 钩子之前,Vue 初始化了生命周期、事件和渲染,此时所有的数据在这个节点还不能拿到

beforeCreate() {
    console.log('--- beforeCreate 钩子 ---');
    console.log('el: ', this.$el);      // undefined
    console.log('data: ', this.$data);  // undefined
},

2. created

beforeCreatecreated 的生命周期之间发生了什么?
image

在这期间初始化了事件,进行数据监测。初始化了 Injections、State(包括了 props、data、methods、computed 和 watch) 和 Provide。此时已经可以访问到 data、props 等数据,但还是没有 el 选项

created() {
    console.log('--- created 钩子 ---');
    console.log('el: ', this.$el);      // undefined
    console.log('data: ', this.$data);  // 有值
},

3. beforeMount

createdbeforeMount 的生命周期之间发生了什么?
image

首先判断对象上是否有 el 选项,如果有就继续往下变异,如果没有,则停止停止生命周期,知道在该 Vue 实例上调用 vm.$mount(el),生命周期才继续执行。

// 在没有el属性的情况下,没有vm.$mount

new Vue({
    beforeCreate: function() {
      console.log('调用了beforeCreate')
    },
    created: function() {
      console.log('调用了created')
    },
    beforeMount: function() {
      console.log('调用了beforeMount')
    },
    mounted: function() {
      console.log('调用了mounted')
    }
})

// 输出 调用了beforeCreate
// 输出 调用了created
// 后续的钩子节点不执行

如果我们在后面调用 vm.$mount(el),则生命周期继续执行

vm.$mount(el);

// 输出 调用了beforeMount
// 输出 调用了mounted

接着判断是否有 template 属性,template 属性对生命周期的影响况:

  • Vue 实例如果有 template 属性,则调用 render 函数去渲染
  • Vue 实例没有 template 属性,则调用外部的 html。实例内部的 template 属性比外部的 html 优先级高
  • 实际上 Vue 对象中还有一个 render 函数,它是以 createElement 作为参数,然后做渲染操作,优先级高于其他
  • 优先级排序:render 函数 > template 属性 > outerHTML

注意,在这个生命周期中,相关的 render 函数首次被调用,但还不能访问到 $el

beforeMount() {
    console.log('--- beforeMount 钩子 ---');
    console.log('el: ', this.$el);      // undefined
    console.log('data: ', this.$data);  // 有值
},

4. mounted

beforeMountmounted 的生命周期之间发生了什么?
image

Vue 实例对象创建 $el 成员,并且替换掉挂在的 DOM 元素。因为在之前 console 中打印的结果可以看到 beforeMount 之前 $el 还是 undefined

5. beforeUpdate 和 updated

image

当一个数据发生改变时,你的视图也将随之改变,整个更新的过程是:数据改变 —— 导致虚拟 DOM 的改变 —— 调用这两个生命钩子去改变视图

这个数据只有和模版中的数据绑定了才会发生更新

6. beforeDestory 和 destoryed

image

在 beferoDestory 生命钩子调用之前,所有实例都可以用,但是当调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

7. 不常用的生命钩子

  • activated:当组件在 keep-alive 中激活的时候调用
  • deactivated:当组件在 keep-alive 中停用的时候调用
  • errorCaptured:这个生命钩子可以看官网,2.5.0之后才有的。当捕获一个来自子孙组件的错误时被调用。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions