Vue实例(对象)有一个完整的生命周期,也就是说从开始创建、初始化数据、编译模板、挂在DOM、渲染-更新-渲染、卸载等一系列过程,我们称为Vue 实例的生命周期,生命周期钩子函数就是在某个阶段给你一个做某些处理,在不同阶段添加自己的代码的机会。
本文通过梳理生命周期全流程的了解生命周期中包含的钩子函数,同时与项目相结合加深钩子函数的理解。
Vue生命周期经典流程图
🟢首先通过 new Vue()创建Vue对象或者说Vue实例,然后初始化一些默认的事件和生命周期
🟡调用beforeCreate() 钩子函数
此时el和data并未初始化,因此无法访问methods, data, computed等上的方法和数据。
🌼官方详情:在实例初始化之后,进行数据侦听和事件/侦听器的配置之前同步调用。
🔴注意:querySelector()方法仅仅返回匹配指定选择器的第一个元素,返回所有的元素使用querySelectorAll()方法。
🟢对inject进行处理,紧接着就做了props、methods、data、computed 和watch的初始化处理。
🟡调用created() 钩子函数
此时的data和methods已经可用,但页面还没有渲染出来,通常操作axios请求和ajax请求,此时已经可以访问this了。在created时可以调用methods中的方法,改变data中的数据,并且可以通过vue的响应式绑定体现在页面上,获取computed中的计算属性。在created()中主要是对实例进行预处理。
🔴注意:这个周期中是没有什么方法来对实例化过程进行拦截的,因此假如有某些数据必须获取才允许进入页面的话,并不适合在这个方法发请求,建议在组件路由钩子beforeRouteEnter()中完成。
🟢判断是否有制定的“el”选项
如果“yes”判断是否有指定template模板。如果有template模板,先渲染模板(将template编译到render函数中),否则就编写#app下的html模板(el外部的html作为template编译)。这一部分执行完成后,把data数据拿到后,并且解析执行模板结构中的指令;当所有指令被解析完毕后,那么页码就会渲染到内存中了;当模板编译完成后,我们的模板页面,还没有挂载到页面上,只是在内存中,用户看不到。
🟡调用beforeMount() 钩子函数
此时el和data已完成初始化,注意此时还没有挂在html到页面上。这是第三个生命周期函数,当模板在内存中执行完成后,会立即执行。此时内存中的模板结构,还没有真正渲染到页面上,此时页面看不到真实的数据。
🌼官方详细:在挂载开始之前被调用,相关的render函数首次被调用。
🟢创建vm.$el 并替换 el。这一步,正在把内存中渲染好的模板结构,替换到页面上,属于正在挂载阶段
🟡调用mounted() 钩子函数
将编译好的模板真实提换到页面中去,此时挂载完成,这个mounted是组件创建阶段最后一个生命周期函数。
此时页面真正的完成了渲染,用户可以看到真实的数据。同时mounted用于初始化第三方UI插件。
即将内存中渲染好的dom元素即 < div id=“app”>ok< /div>已经 提换了页面上的 < div id=“app”>{{msg}} < /div>
🌼官方详细:实例被挂载后调用,这时 el 被新创建的 vm.$el 替换了。如果根实例挂载到了一个文档内的元素上,当 mounted 被调用时 vm.$el 也在文档内。
注意 mounted 不会保证所有的子组件也都被挂载完成。如果你希望等到整个视图都渲染完毕再执行某些操作,可以在 mounted 内部使用 vm.$nextTick。该钩子在服务器端渲染期间不被调用。
🔴注意:关于dom的操作要放在mounted里面,在mounted前面访问dom会是undefined
🟢组件运行中的生命周期函数回根据Data数据的变化,有选择性的触发。
🟡调用beforeUpdate() 钩子函数
当data数据改变时,即完成data(model层) ->view(视图层)的更新。当执行beforeUpdate运行的时候,先在内存中渲染一份最新的dom树,data中的数据都是最新的,但是页面上的数据还是旧的,页面和最新的数据尚未保持同步。源码中如果当前的Vue实例的_isMounted为true的话,直接调用beforeUpdate钩子。该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务器进行。
🔴注意:_isMounted在mounted钩子执行前就已经设置为true了。
🟢执行完beforeUpdate后虚拟DOM重新渲染并应用更新
🟡调用updated() 钩子函数
源码中因为会有很多各组件,因此会有多个watcher,callUpdatedHooks会判断检查当前的watcher是哪个,是当前的话,就直接执行当前updated钩子。将最新的dom树重新渲染到真实的页面上去,页面更新完后,此时data数据是最新的,同时页面上呈现出的数据也是最新的。
🌼官方详细:在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后被调用。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态。如果要相应状态改变,通常最好使用计算属性或 watcher 取而代之。
🔴注意:updated 不会保证所有的子组件也都被重新渲染完毕。如果你希望等到整个视图都渲染完毕,可以在 updated 里使用 vm.$nextTick:
🟢当调用vm.$destory()函数时进行销毁阶段
🟡调用beforeDestroy() 钩子函数
当执行beforeDestory的时候,组件即将被销毁,但是还没有真正开始销毁,只是销毁前的操作,组件还可以正常可用,还可以用this来获取实例。在卸载前检查组件是否已被卸载,如果已经被卸载直接return出去。
🟢解除绑定,销毁子组件以及事件监听器,此时是销毁过程,正在销毁中。
🟡执行destroyed() 钩子函数
组件销毁完毕,data和methods都不可用。
🌼官方详细:实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
总结
触发钩子的完整顺序:
将路由导航、keep-alive、和组件生命周期钩子结合起来的,触发顺序,假设是从a组件离开,第一次进入b组件:
-
beforeRouteLeave:路由组件的组件离开路由前钩子,可取消路由离开。 -
beforeEach: 路由全局前置守卫,可用于登录验证、全局路由loading等。 -
beforeEnter: 路由独享守卫 -
beforeRouteEnter: 路由组件的组件进入路由前钩子。 -
beforeResolve:路由全局解析守卫 -
afterEach:路由全局后置钩子 -
beforeCreate:组件生命周期,不能访问this。 -
created:组件生命周期,可以访问this,不能访问dom。 -
beforeMount:组件生命周期 -
deactivated: 离开缓存组件a,或者触发a的beforeDestroy和destroyed组件销毁钩子。 -
mounted:访问操作dom。 -
activated:进入缓存组件,进入a的嵌套子组件(如果有的话)。 -
执行
beforeRouteEnter回调函数next。
参考文章: