Vue 实例 --- 实例生命周期钩子

什么是生命周期钩子?

每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。

理解:支持用户定义函数,并在这个函授内部可以访问到实例的属性(设置this),这个函数会在实例实例化的过程中被调用。

生命周期钩子函数有哪些?
名称触发时机
beforeCreate在实例初始化之后调用。
created在实例创建完成后被立即调用。
beforeMount在挂载开始之前被调用。
mounted实例被挂载后调用。
beforeUpdate数据更新时之后,虚拟DOM更新和页面重新渲染之前调用。
updated数据更改导致的虚拟DOM更新和页面重新渲染之后调用。
beforeDestroy实例销毁之前调用。
destroy实例销毁后调用。
activated被 keep-alive 缓存的组件激活时调用。
deactivated被 keep-alive 缓存的组件停用时调用。
简单应用
beforeCreate

此函数被调用的时候,内部this还有没有修改,即无法直接通过实例加点的方式访问data,props,methods,computed等。

那么此时能干点什么?实际开发中我只有用到一次,因为能在这里做的事情,如果不涉及到Vue实例,那么完全可以在Vue实例之前完成,比如根据url参数设置Vue实例的初始值。

var  query = geturlquery();
var data =  {
        num:query.num ? query.num : 0 
    }
new Vue({
    el:"#app",
    data:data
})

第二种,对vue实例配置进行修改,原则上是不被允许的允许的,因为容易出错,你传的opts已经过Vue的处理过挂到$options上了,所以你这个时候去处理很容易出问题。
例如:你的data已经被处理成了一个函数,那么我们按照Vue的表现尝试下修改.

        new Vue({
            el:"#app",
            data:{
                test:"test"
            },
            beforeCreate() {
                console.log(this);
                this.$options.data = function(){
                    return {
                        title:"title"
                    }
                }
            },
        })

这里当然是成功了的,实例化之后只有一个title数据在$data上,但是请不要使用这方法,这种方式我们按照内部运行机制进行修改的,当然理所应该是可以的。
但是请注意,我们并不能保证这种内部运行机制是不变的,并且Vue也没有保证这个,Vue能保证的是你传入的opts和完成实例化的实例的表现是固定不变的。
而这种不规范的改变,在版本晋升之后是否能保证是谁也不能保证的。

下面说说我用到的情况,是结合vue-router一起使用的,在spa页面中,我们在beforeCreate这里已经可以通过this.$router获取到跳转之后的router和它携带的参数,可以通过参数进行一些控制操作,如果不允许访问,直接跳转离开。

        beforeCreate(){
           if(!this.$route.params.allowed){
               this.$router.go(-1)
           }
           
        },

这个操作跟上面的通过query进行操作类似,但是相比在外部进行操作,this.$router无疑是最方便获取路由信息的方式,而相对后面的其他周期,beforeCreate是最先的周期,能更早的进行判断。

created

在这个周期里面,我们传入的数据和方法,已经被初始化到实例上了,并且已经完成了数据监测的双向绑定机制,所以这个阶段可以进行操作数据和使用方法。

所以,个人认为这个阶段我们去请求初始化的ajax数据是最好的,原因是最早,尽可能的减少用户等待时间。
如果再mounted之前能返回,我们就直接看见数据,不用走更新流程。
如果再mounted之后,走数据改变更新视图流程。
至于其他的,能在上个钩子中的做的事情,这个都可以做。
但是注意这个时候实例还没有挂载,所以不存在DOM的,基于DOM的一些操作和插件的初始化这里不能进行。

beforeMount

我一直感觉是这个很尴尬的钩子,这钩子相比前一个钩子函数。
首先多了一个$el属性,但是这个$el跟我们常使用的那个$el还有点不一样,他是原始传入的那个DOM,目前还不是我们最终挂载到页面的那个DOM,看下面代码:

        new Vue({
            el:"#app",
            data:{
                test:"test"
            },
            beforeMount() {
                console.log(this.$el);
            },
        })

        // <div id="app">{{test}}</div>

有谷歌调试经验的应该知道,谷歌控制台打印出来的DOM的,鼠标移上去的时候会标记当前页上的位置,但是这个没有。
所以这个$el是将来挂载的时候,被替换的那个DOM。那么问题来了,我能拿它干点啥,毕竟下个周期就要被替换了。
除了这个之外,还有了一个重要函数,_render函数,这个函数根据html模板生成的渲染函数,最后会生成DOM渲染页面过程会调用这个函数。那么问题又来了,我难道还能修改这个函数吗?

   new Vue({
            el:"#app",
            data:{
                test:"test"
            },
            beforeMount() {
                this._render = () => this.$createElement('h1', null, 'It works!')
            },
   })

嗯,事实证明我们可以修改这个函数,那么问题又来了,我们那么大段的模板代码编译成了一个渲染函数,我们这里改它干什么?如果说要手写_render函数,也可以在传入的opts中增加一个render函数就行了。
这个阶段多的属性和方法,暂时没想到有什么用处。

mounted

这个生命钩子被调用的时候,代码Vue实例已经成功挂载在页面上了,$el已经替换成生成的DOM,并且替换掉了页面的原来的$el,并且对应的数据和视图形成双向绑定。请注意这个过程是替换,所以官方要求我们,不要直接初始化在body元素上。

此时,可以访问所有Vue提供给我们的方法和属性了,可以访问到生成之后的DOM了。

基于的DOM的插件也可以在这里初始化了。

beforeUpdate

首先,需要确定的是,Vue的数据更新导致视图更新是一个异步的过程,在一次事件循环过程中,无论改变了多少个数据或同一个数据改变了多少次,最终的只会执行一次视图更新。
而beforeUpdate是在视图更新前调用,所以在这里我们能取到所有改变的数据,并且可以进行最后一次修改。

updated

这个生命周期钩子会在视图更新之后调用,也就是说,在这里钩子中,我们能取到更新之后的生DOM元素,所以在一些基于DOM的插件中,这个周期可以刷新插件,使之监听到新增的DOM和删除的DOM。

当我们调用$destroy,会触发两个生命周期,beforeDestroydestroyed

beforeDestroy

当调用$destroy,就会立即调用这个钩子,所以在这个阶段,我们还可以通过正常操作Vue。

网上大多数推荐在这周期进行解绑$on监听的事件(特别是使用了eventhub),清楚定时器,解绑dom事件。

当然,这些处理是完全可以的,但是相对于这些处理,我更倾向于把这些操作放到destroyed中做,因为那个是时候才真正意味着这个实例的销毁,那么处理Vue没给我们处理的一些事件,定时器的解绑是不是更合理一点?

那么这个周期能干什么?个人认为,这个周期官方特意注明了。这个时候,还能正常的操作Vue,而Vue触发销毁之后是不会中断的,所以这个时候我们的能干什么?用狼人杀的逻辑,我已经被砍了,那么当我还在的时候(最后能正常发言的时候),能做的只有留遗言,通知跟我关系的(手动监听了我的其余实例或者方法),告诉他们我要销毁了,我这边的联系我会解除,你们赶紧解除跟我的关联吧啊(不是vue自带的),是不是合理一点。

destroyed

实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。

但是实际上

callHook(vm, 'destroyed');
// turn off all instance listeners.
vm.$off();

这周周期钩子触发完成之后才会解绑事件,但是需要特别注意的是,这里解绑的是,父子组件之间的自定义事件,如果给一个DOM绑定一个click事件,即使这个事件是用过@绑定的,这个事件也不会被销毁,看到这里大家也就清楚,原生事件不会被解除,那么已经渲染到页面上的DOM那么就更不会自动回收移除了。

那么这个$destroy()到底干了什么呢?解除Vue组件之前的父子关联,解除自定义事件的监听,解除watch的观察,最重要的是,解除页面和实例的双向绑定,最后解除Vue实例化过程对Vm的指向占用,而js的收回机制,当一个对象没有指向占用的时候,会被自动回收。

所以这阶段,我们需要做的是对这个$destroy方法进行补充操作,

  1. 对初始化的基于DOM的插件进行销毁,解除实例存放的变量的占用
  2. 需要保存DOM的时候手动解绑DOM的实践监听
  3. 不需要保存DOM就直接移除掉所有DOM

还有两个在使用vue-router并且使用keepalive缓存路由页面的才会触发的钩子函数deactivatedactivated

activated

比较的经典使用经常就是缓存滚动条的位置。大家都知道,vue-router切换不同路由的时候,即使我们使用keey-alive也不会缓存滚动的位置,所以这个时候需要只要在滚动的时候记住scrollTop存放在当前实例中,因为我们使用的缓存,所以当切换到其他路由的时候这个实例也不会销毁,当再次激活的时候,会触发这个周期,我们只需要吧scrollTop赋值给滚动元素,就简单的缓存的滚动条的位置。

        // just like this
        mounted () {
            this.scrollEl.onscroll = () => {
                this.scrollTop = this.scrollEl.scrollTop;
            }
        },
        activated () {
            this.$refs.scrollEl.scrollTop = this.scrollTop;
        }

另一个经典的使用场景是,缓存页面中,根据路由参数或者状态局部刷新路由页面的数据。

deactivated

当这个周期被激活的时候,有一个意思的现象,在这个周期访问this.$router会发现已经更新成跳转之后的路由而不是当前实例的路由,而this访问到还是当前实例。那么周期,我们就可以根据去往的路由页面,对当前实例进行一些操作。

比如,当前页面是详情,详情下面还有一个编辑页,那么当前页面如果从列表中进来的时候设置了缓存,如果进入编辑,我们也需要缓存当前页面,返回的时候activated周期进行局部刷新。而返回的到列表的时候,需要销毁这个页面的缓存,就可以这样。

    activated(){
        this.loadDetail();
    },
    deactivated() {
        if(this.$route.name == 'index'){
            this.removeKeepAlive('detail');
        }
    }
        

欢迎交流~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值