pocket-manual icon indicating copy to clipboard operation
pocket-manual copied to clipboard

关于 Vue 实例的生命周期

Open FishPlusOrange opened this issue 7 years ago • 0 comments

每一个 Vue 实例都有一个完整的生命周期,其包括实例创建、设置数据监听、编译模板、将实例挂载到 DOM以及在数据变化时更新 DOM 等。

Vue 实例的生命周期简单概括就是从创建到挂载再到销毁,以及过程中的更新。

在整个生命周期过程中,Vue 提供了一些生命周期钩子,给了我们在不同阶段执行自定义逻辑的机会。

接下来总结一下生命周期的概念以及生命周期钩子的使用。

话不多说,先来看一个例子(JSFiddle):

<div id="app">
  <p>{{ message }}</p>
</div>
new Vue({
  el: "#app",
  data() {
  	return {
    	message: 'Here goes lifecycle hooks demo'
    }
  },
  beforeCreate() {
    console.group('%cbeforeCreate:', 'color:#3ab882')
    console.log(this.$el && this.$el.innerHTML)
    console.log(this.$data)
    console.log(this.message)
    console.groupEnd()
  },
  created() {
    console.group('%ccreated:', 'color:#3ab882')
    console.log(this.$el && this.$el.innerHTML)
    console.log(this.$data)
    console.log(this.message)
    console.groupEnd()
  },
  beforeMount() {
    console.group('%cbeforeMount:', 'color:#3ab882')
    console.log(this.$el && this.$el.innerHTML)
    console.log(this.$data)
    console.log(this.message)
    console.groupEnd()
  },
  mounted() {
    console.group('%cmounted:', 'color:#3ab882')
    console.log(this.$el && this.$el.innerHTML)
    console.log(this.$data)
    console.log(this.message)
    console.groupEnd()
    this.message = 'The message has been changed' // 触发 update
  },
  beforeUpdate() {
    console.group('%cbeforeUpdate:', 'color:#3ab882')
    console.log(this.$el && this.$el.innerHTML)
    console.log(this.$data)
    console.log(this.message)
    console.groupEnd()
  },
  updated() {
    console.group('%cupdated:', 'color:#3ab882')
    console.log(this.$el && this.$el.innerHTML)
    console.log(this.$data)
    console.log(this.message)
    console.groupEnd()
    this.$destroy() // 触发 destroy
  },
  beforeDestroy() {
    console.group('%cbeforeDestroy:', 'color:#3ab882')
    console.log(this.$el && this.$el.innerHTML)
    console.log(this.$data)
    console.log(this.message)
    console.groupEnd()
  },
  destroyed() {
    console.group('%cdestroyed:', 'color:#3ab882')
    console.log(this.$el && this.$el.innerHTML)
    console.log(this.$data)
    console.log(this.message)
    console.groupEnd()
    
    // 实例销毁后
    this.message = 'The message has been changed again' // 触发 update
    console.group('%cafterDestroyed:', 'color:#3ab882')
    console.log(this.$el && this.$el.innerHTML)
    console.log(this.$data)
    console.log(this.message)
    console.groupEnd()
  }
})

运行结果:

lifecycle-hooks-demo

  • beforeCreate

    从控制台的输出结果可以看到,$el$data 在这个阶段并未初始化,均返回 undefined

    该钩子函数在实例初始化之后、data observer 和 event/watcher 事件配置之前被调用。

  • created

    从控制台的输出结果可以看到,这个阶段完成了数据的初始化,所以 $datamessage 均可访问,但是 $el 未初始化,其返回 undefined

    该钩子函数在实例创建完成之后被调用,实例完成 data observer、属性和方法的运算以及 watch/event 事件回调。但是还未进入挂载阶段,$el 属性不可见,正是以上 $el 返回 undefined 的原因。一般我们会在这个阶段做一些数据请求操作。

  • beforeMount

    从控制台的输出结果可以看到,这个阶段完成了 $el$data 的初始化,所以相关属性均可访问。

    该钩子函数在挂载之前被调用,相关 render 函数首次被调用,Vue 实例完成了模板编译并结合数据生成 HTML,但是还未挂载到页面,所以此时 this.$el.innerHTML 输出为 <p>{{ message }}</p>

  • mounted

    从控制台的输出结果可以看到,这个阶段完成了挂载。

    该钩子函数在 el 被新创建的 vm.$el 替换并挂载到实例上之后被调用,用编译好的 HTML 替换 el 指向的 DOM 对象并渲染到页面上。

  • beforeUpdate

    代码中,我通过修改 message 触发数据更新,beforeUpdate 先被调用。

    该钩子函数在数据更新之前被调用,所以此时 this.$el.innerHTML 输出为 <p>Here goes lifecycle hooks demo</p>,其仍为修改前的数据。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。

    如果在该钩子函数中进一步修改状态,由于 Vue 异步更新有去重机制,不会触发附加的重渲染过程。

  • updated

    从控制台的输出结果可以看到,这个阶段完成了数据更新。

    该钩子函数在由于数据修改导致虚拟 DOM 重新渲染和 patch 之后被调用,调用时组件 DOM 已经完成更新,所以一般可用于执行一些依赖于更新后 DOM 的操作。

    但是在大多数情况下,我们应该避免在此期间更改状态,其可能导致更新进入死循环。如果确实需要更改状态,可以尝试使用 computedwatch

  • beforeDestroy

    代码中,我通过调用 $destroy 方法销毁实例,beforeDestroy 先被调用。

    该钩子函数在实例被销毁之前被调用,此时实例仍然可用,所以相关属性仍可访问。

  • destroyed

    该钩子函数在实例被销毁之后被调用。调用后,所有的事件监听器都会被移除,所有的子实例也会被销毁。此时 View 层和 Modal 层解绑,失去响应式,下面将会进一步进行验证。

  • afterDestroyed

    这里所谓的 afterDestroyed 并不属于 Vue 实例的生命周期,其主要是为了说明 在实例被销毁之后页面的状态。

    destroyed 钩子函数中,我再次通过修改 message 以触发数据更新。从控制台的输出结果可以看到,实例仍然存在且可用,$data 属性更新了,message 被修改为 The message has been changed again,但是 $el 并未更新,仍为 <p>The message has been changed</p>,可见页面不再具有响应式。

最后,放一张官方的生命周期图示:

lifecycle

参考:

FishPlusOrange avatar Aug 19 '18 09:08 FishPlusOrange