一. 在beforeCreate生命周期钩子被调用之前,其实还有两个阶段:
(1) 编译阶段。
如果使用了构建步骤,例如使用了单文件组件【.vue文件】,则会在此阶段进行:
对模板进行解析和编译,并生成渲染函数。编译过程包括 将模板转换为虚拟DOM、解析指令和表达式等,生成具有响应式能力的render函数等工作。这个阶段会处理组件的模板和定义,生成组件的描述对象。
(2) new Vue()【组件实例化阶段的开始】
调用了new Vue()后,会根据组件的描述对象,创建对应的组件实例,并初始化一些生命周期钩子和事件。
二. Vue2生命周期钩子调用顺序简述如下:
(1) beforeCreate()
调用时机:
组件实例创建之后,进行初始化之前调用。
当调用此生命周期钩子时:
组件实例还未对选项进行处理,data 和 methods还不能访问。
当调用此生命周期钩子后:
会对组件选项进行处理,例如进行数据响应式注入、创建计算属性。
(2) created()
调用时机:
组件实例已经初始化完成【已经完成对选项的处理】。
当调用此生命周期钩子时:
组件实例已经初始化完成【已经完成对选项的处理】,意味着 组件 数据侦听【data里的变量定义为了响应式变量】、计算属性【computed】、方法【methods】、事件/侦听器的回调函数【watch / event listeners】 已经被配置完毕,此时可以访问组件实例的data 和 methods。
此时挂载阶段还没有开始,模板还没有被渲染成HTML,vm.$el还不可用。
当调用此生命周期钩子后:
会判断是否有挂载目标【即是否有el选项】,
如果有挂载目标,则会进一步判断组件是否有template选项;
如果没有挂载目标,则会在给组件实例指定挂载目标【即vm.$mount(el)被触发】后再判断组件是否有template选项。
在判断组件是否有template选项时:
如果有template选项,则会将template选项里的内容编译成渲染函数;
如果没有template选项,则会将挂载的dom节点的outerHTML【即包含该元素自身标签的完整HTML字符串】当作template,并编译成渲染函数,这意味着Vue会解析挂载元素的标记和内容,并根据其创建相应的DOM元素结构。这种方式使得Vue可以直接使用已有的HTML结构作为初始模板而无需额外定义。
注意:
在单文件组件中,created()生命周期钩子之后不会再做与 挂载目标元素 或 模板选项 相关的判断,因为在使用单文件组件【.vue文件】时,Vue的编译过程会提前处理模板选项,并将其编译为渲染函数。
应用场景:
将子组件里获取下拉框列表里的数据请求放到父组件的created方法里异步执行,然后将结果传给子组件,这样可以进一步缩短展示子组件时不必要的延迟时间,并进一步避免一些因为过长的延迟时间导致的问题。
(3) beforeMount()
调用时机:
组件挂载到DOM 开始之前 调用。
当调用此生命周期钩子时:
组件实例 已经与 模板 或 挂载目标元素 关联,并可执行一些逻辑操作。
当调用此生命周期钩子后:
会首次调用渲染函数生成虚拟DOM【VNode】,并进行首次渲染【用虚拟DOM树替换容器的真实DOM树(将el用vm.$el替换掉)】。
当组件挂载到了DOM之后,就会调用mounted生命周期钩子。
(4) mounted()
调用时机:
el 已被新创建的 vm.$el 替换掉。
当调用此生命周期钩子时:
组件模板已经被渲染成HTML并且挂载到了页面上,此时响应式数据 和 视图 已经完成了双向绑定。
当调用此生命周期钩子后:
如果响应式数据发生变化,就会调用beforeUpdate生命周期钩子。
注意:
mounted 不会保证所有的子组件也都被挂载完成。如果希望等到整个视图都渲染完毕再执行某些操作,可以在 mounted 内部使用 vm.$nextTick:
mounted: function () {
this.$nextTick(function () {
// 仅在整个视图都被渲染之后才会运行的代码
})
}
(5) beforeUpdate()
调用时机:
在响应式数据发生变化之后,虚拟DOM重新渲染之前调用。
当调用此生命周期钩子时:
响应式数据已经发生了变化,但虚拟DOM还没有立即更新。这是因为当响应式数据发生变化时,Vue会调度一个异步的更新过程,在这个更新过程中,Vue会对所有发生变化的组件进行批量的更新操作【将多个更新操作合并为一次更新】,而不是立即更新每个组件的虚拟DOM,这样做可以避免频繁地更新真实DOM,从而提高性能。
当调用此生命周期钩子后:
会采用diff算法对比VNode 和 真实DOM 的差异,并将虚拟DOM中变化的部分 更新到真实dom上,使页面显示与最新的VNode保持一致。
完成更新后,就会调用updated()生命周期钩子。
应用场景:
获取到更新前的状态,在 现有DOM 将要被更新之前访问它,比如移除手动添加的事件监听器。
注意:
此时更改响应式数据将不会触发新一轮的更新,因为已经进入了更新阶段。
(6) updated()
调用时机:
组件DOM树已更新。
当调用此生命周期钩子时:
组件 DOM 已经更新,所以此时可以执行依赖于 DOM 的操作。
当调用此生命周期钩子后:
如果vm.$destroy()方法被触发调用,就会调用beforeDestroy()方法。
注意:
在大多数情况下,应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
updated 不会保证所有的子组件也都被重新渲染完毕。如果希望等到整个视图都渲染完毕,可以在 updated 里使用 vm.$nextTick:
updated: function () {
this.$nextTick(function () {
// 仅在整个视图都被重新渲染之后才会运行的代码
})
}
(7) beforeDestroy()
调用时机:
组件即将被销毁。
当调用此生命周期钩子时:
组件实例仍然完全可用,还可以访问data和methods。
当调用此生命周期钩子后:
完全销毁该组件实例。清理它与其它实例的连接,卸载 该组件及该组件的子组件,解绑这些组件 绑定的 侦听器【watchers】、 全部指令 及 事件监听器【event listeners】。
完成了卸载后,就会调用destroyed()生命周期钩子。
(8) destroyed()
调用时机:
组件及其子组件 实例 都已被销毁。
当调用此生命周期钩子时:
该组件实例及其子组件实例 已经被销毁,相关的侦听器、指令和 事件监听器已经被移除。