Vue生命周期梳理

757 阅读6分钟

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组件:

  1. beforeRouteLeave:路由组件的组件离开路由前钩子,可取消路由离开。

  2. beforeEach: 路由全局前置守卫,可用于登录验证、全局路由loading等。

  3. beforeEnter: 路由独享守卫

  4. beforeRouteEnter: 路由组件的组件进入路由前钩子。

  5. beforeResolve:路由全局解析守卫

  6. afterEach:路由全局后置钩子

  7. beforeCreate:组件生命周期,不能访问this

  8. created:组件生命周期,可以访问this,不能访问dom。

  9. beforeMount:组件生命周期

  10. deactivated: 离开缓存组件a,或者触发a的beforeDestroydestroyed组件销毁钩子。

  11. mounted:访问操作dom。

  12. activated:进入缓存组件,进入a的嵌套子组件(如果有的话)。

  13. 执行beforeRouteEnter回调函数next。

参考文章:

Vue的钩子函数[路由导航守卫、keep-alive、生命周期钩子]

详解 Vue 生命周期实现

Vue - 生命周期详解

vue之生命周期

Vue 生命周期详解

Vue.js官方文档生命周期钩子