Skip to content
On this page

Vue的生命周期方法

  • 研究方向

    • hook机制
  • 研究方法

    • 各个hook在哪个阶段触发,为啥会在这个阶段触发?触发这个钩子可以改变什么或者得到什么?

钩子函数(Hook)

钩子是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息; 钩子机制允许应用程序截获处理window消息或特定事件, 钩子函数可以理解为js运行进程中在其特定段时的回调函数

什么是生命周期?

生命周期:Vue 实例从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期,各个阶段有相对应的事件钩子 vue 的生命周期

  • 图中 数据观测:

在new Vue() 时 开始观测数据

  • init event & lifeCycle: 表示new Vue 后事件和生命周期的init
  • created完成之后,它会去判断instance(实例)里面是否含有“el”option(选项),如果没有的话,它会调用vm.$mount(el)这个方法,然后执行下一步;如果有的话,直接执行下一步。紧接着会判断是否含有“template”这个选项,如果有的话,它会把template解析成一个render function ,这是一个template编译的过程,结果是解析成了render函数:
render (h) {
  return h('div', {}, this.text)
}

render函数里面的传参h就是Vue里面的createElement方法,return返回一个createElement方法,其中要传3个参数,第一个参数就是创建的div标签;第二个参数传了一个对象,对象里面可以是我们组件上面的props,或者是事件之类的东西;第三个参数就是div标签里面的内容,这里我们指向了data里面的text。

使用render函数的结果和我们之前使用template解析出来的结果是一样的。render函数是发生在beforeMountmounted之间的,这也从侧面说明了,在beforeMount的时候,$el还只是我们在HTML里面写的节点,然后到mounted的时候,它就把渲染出来的内容挂载到了DOM节点上。这中间的过程其实是执行了render function的内容。

在使用.vue文件开发的过程当中,我们在里面写了template模板,在经过了vue-loader的处理之后,就变成了render function,最终放到了vue-loader解析过的文件里面。这样做有什么好处呢?原因是由于在解析template变成render function的过程,是一个非常耗时的过程,vue-loader帮我们处理了这些内容之后,当我们在页面上执行vue代码的时候,效率会变得更高。

beforeMount在有了render function的时候才会执行,当执行完render function之后,就会调用mounted这个钩子,在mounted挂载完毕之后,这个实例就算是走完流程了。 后续的钩子函数执行的过程都是需要外部的触发才会执行。比如说有数据的变化,会调用beforeUpdate,然后经过Virtual DOM,最后updated更新完毕。当组件被销毁的时候,它会调用beforeDestory,以及destoryed

生命周期的钩子 (lifecyclehook)

Vue所有的生命周期钩子自动绑定在this上下文到实例中,因此你可以访问数据,对属性和方法进行运算。这意味着你不能使用箭头函数来定义一个生命周期方法。这是因为箭头函数绑定了父上下文,因此this与你期待的Vue实例不同。

生命周期中的钩子函数组建状态最佳实践
beforeCreate实例初始化之后,this指向创建的实例,此时还不能访问到data,computed,watch,methods上的数据和方法常用于初始化非响应式变量
created实例创建完成,可以访问到 data,computed,watch,methods上的数据和方法,未挂载到dom 上,不能访问#el 属性, $ref属性d额内容为空数组常用语简单的ajax请求,页面的初始化
beforeMount在挂载开始前被调用,beforeMount之前,会找到对应的template,并编译成render函数-
mounted实例挂载到DOM上,此时可以通过DOM API 获取到DOM 节点,$ref 属性可以访问常用于获取vnode信息和操作,ajax请求
beforeUpdate响应式数据更新时调用,发生在虚拟DOM打补丁之前适合在更新之前访问现有的DOM,比如手动移除已添加的事件监听器
updated虚拟DOM重新渲染和打补丁之后调用,组件DOM已经更新,可执行依赖于DOM的操作避免在这个钩子函数中操作数据,可能陷入死循环
beforeDestroy实例销毁之前调用。这一步,实力任然完全可用,this仍能获取到实例常用于销毁定时器,解绑迁居时间,销毁插件对象等操作
destroyed实力销毁后调用,调用后,Vue实例只是的所有东西都会解绑,所有的事件监听器会被移除,所有的姿势里也会被销毁在此也可以用于销毁定时器

注:

  • created阶段的ajax请求与mounted请求的区别:前者页面视图未出现,如果请求信息过多,页面会长时间处于白屏状态,此时如果做DOM请求时会报错,页面还没有渲染出来

  • mounted 不会承诺所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以用 vm.$nextTick (https://blog.csdn.net/sinat_17775997/article/details/71638144)

  • beforeCreatecreatedbeforeMountmounted 是自动被调用

  • beforeUpdate, updated,beforeDestroy,destroyed 是手动调用

  • 子组件的created 优先于父组件的created 执行, 父组件的beforeCreate 优先于子组件的beforeCreate

  • 子组件的mounted优先于父组件的mounted, 父组件的beforeMount 优先于子组件的 beforeMount

  • 子组件的destroyed优先于父组件的beforeDestroy, 父组件的beforeDestroy 优先于子组件的 beforeDestroy

activated & deactivated

最后还有 两个钩子函数没显示:activated & deactivated 使用<keep-alive>会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在activated阶段获取数据,承担原来created钩子中获取数据的任务

  • activated: 在组件被激活时调用,在组件第一次渲染时也会被调用,之后每次keep-alive激活时被调用,该钩子在服务器端渲染期间不被调用。

  • deactivated: keep-alive组件停用时调用。该钩子在服务端渲染期间不被调用。

    组件内的离开当前路由钩子beforeRouteLeave => 路由前置守卫 beforeEach => 全局后置钩子afterEach => deactivated 离开缓存组件 => activated 进入缓存组件(如果你进入的也是缓存路由) // 如果离开的组件没有缓存的话 beforeDestroy会替换deactivated // 如果进入的路由也没有缓存的话 全局后置钩子afterEach=>销毁 destroyed=> beforeCreate等

路由导航守卫

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

实例demo: 见template

参考文档:
如何解释vue的生命周期才能令面试官满意?
vue生命周期(五)
深入 Vue 生命周期
Vue 实例中的生命周期钩子详解
Vue.js源码解读
生命周期Vue的钩子函数[路由导航守卫、keep-alive、生命周期钩子]
Vue生命周期中mounted和created的区别
深入理解Vue生命周期、手动挂载及挂载子组件

遗留问题:

  • 兄弟组件通过bus传值时的updated,beforeUpdate 生命周期顺序
    如果是通过父级派发更新事件时,beforeUpdate,和updated的顺序 都是自上到下正常顺序进行;
    如果是兄弟组件的派发顺序是beforeUpdate是从上到下,updated 的顺序是从下到上的更新顺序

  • beforeCreate 有什么具体实际的用处,例举
    vue生命周期中 beforeCreate 是实例初始化完成后数据观测(data observer)和event/watcher之前被调用。

    在beforeCreate前,所有的options都会先存到vm.$options中,在beforeCreate之后,将$options里的data啦,props啦,methods啦等等一个个附到vm上,然后再触发created钩子。所以在beforeCreate的时候,通过this.message是拿不到值的,在created的时候就能通过this.message拿到值了。

    一定要在beforeCreate的时候就同步去拿data里的值的话,就是直接从this.$options.data里去拿。如果data中的初始值是简单的string,那直接this.$options.data()["message"]就好.涉及到复杂点的情况,建议看看源码里是怎么处理的,具体在core/instance/state.js中的initData(vm)里。 还有一种方法 可以使用nextTick(),相当于做个延迟,等页面挂载完后在执行 使用promise

    javascript
    let F = null;
    export default {
        //...
        data(){
            return {
                dataList:[]
            }
        },
        beforeCreate() {
            F = fetchData(//fetchData是取数方法
                //...返回promise
            )
        },
        created() {
            F.then(data=>{
                this.dataList = data
            })
        }
    }
    

    本人理解其中原因就是一个实例初始化后的提示功能,表示此后要开始