1、生命周期钩子函数
生命周期: Vue 实例从创建到销毁的过程,就是生命周期;就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期
由于本文章不是着重去讲解生命周期钩子函数所以下面简单介绍一下生命周期的11个钩子函数
上图是vue官方文档里的声明周期图,其中上面8个红色圆角矩形是vue生命周期最基本的八个钩子函数,下面来解释下这八个钩子函数,并扩展剩余三个
beforeCreate:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用,此时拿不到data里定义的变量数据
created:在实例创建完成后被立即调用。在这一步,实例已完成以下的配置:数据观测 (data observer), 属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
beforeMount:在挂载开始之前被调用:相关的 render 函数首次被调用
mounted:el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内
beforeUpdate:数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行
updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子
activated:keep-alive 组件激活时调用。该钩子在服务器端渲染期间不被调用
deactivated:keep-alive 组件停用时调用。该钩子在服务器端渲染期间不被调用
beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用
destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用
errorCaptured:当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播
<template>
<div ref="index">b组件</div>
</template>
<script>
export default {
name: "",
data() {
return {
str: "我是变量str,被挂载啦-------",
};
},
beforeCreate() {
console.log(this.str, this.$refs.index);
console.log('\x1b[35m%s\x1b[0m',"我是b组件的beforeCreate");
},
created() {
console.log(this.str, this.$refs.index);
console.log('\x1b[35m%s\x1b[0m',"我是b组件的created");
},
beforeMount() {
console.log(this.str, this.$refs.index);
console.log('\x1b[35m%s\x1b[0m',"我是b组件的beforeMount");
},
mounted() {
console.log(this.str, this.$refs.index);
console.log('\x1b[35m%s\x1b[0m',"我是b组件的mounted");
},
beforeUpdate() {
console.log(this.str, this.$refs.index);
console.log('\x1b[35m%s\x1b[0m',"我是b组件的beforeUpdate");
},
updated() {
console.log(this.str, this.$refs.index);
console.log('\x1b[35m%s\x1b[0m',"我是b组件的updated");
},
beforeDestroy() {
console.log(this.str, this.$refs.index);
console.log('\x1b[35m%s\x1b[0m',"我是b组件的beforeDestroy");
},
destroyed() {
console.log(this.str, this.$refs.index);
console.log('\x1b[35m%s\x1b[0m',"我是b组件的destroyed");
},
};
</script>
<style lang="less" scoped></style>
上面是八个常见钩子函数代码,下面进行简单解析
如上图执行了4个钩子函数,其中变量在created上才被拿到,DOM元素在挂载后才能被拿到(即mounted钩子函数中)
由于activated和deactivated钩子函数是在动态组件激活和停用时才触发的(即keep-alive 组件 ),不做过多讲解(具体钩子函数讲解请看以下博客),下面看看销毁钩子函数
下面我封装了两个组件,进行组件间的切换来达到销毁和生成过程(注意本文重点来了)
按照理想状态从b组件切换到a组件钩子函数执行过程依次是:
beforeDestroy(b)==>destroyed(b)==>
beforeCreate(a)==>created(a)==>beforeMount(a)==>mounted(a)下面看看实际运行结果:
beforeCreate(a)==>created(a)==>beforeMount(a)==>
beforeDestroy(b)==>destroyed(b)==>mounted(a)
下面我将a组件中的变量传入到b组件,并在b组件beforeDestroy和destroyed中打印
那怎么让b先销毁然后a在创建呢
在销毁的组件里加上路由钩子函数(beforeRouteLeave)
beforeRouteLeave(to, from, next) { this.$destroy(); next(); },
执行结果如下:
下面就来讲讲路由钩子函数
2、路由钩子函数
路由钩子函数分为三大块共7个钩子
全局钩子:
- 前置守卫:router.beforeEach
- 后置守卫:router.afterEach
- 解析守卫:router.beforeResolve
解析守卫(router.beforeResolve)是在所有组件内守卫和异步路由组件被解析完后才被调用
路由独享钩子(我常常叫路由配置钩子) :
beforeEnter:单个的路由钩子,是在路由配置上直接定义的
const router = new VueRouter({ routes: [ { path: "/a", component: () => import("@/components/a.vue"), beforeEnter: (to, from, next) => { console.log("beforeEnter"); //........ next(); }, }, ], });
组件内路由钩子:
- beforeRouteEnter
- beforeRouteUpdate
- beforeRouteLeave
这些钩子函数直接在路由组件内部直接定义的
下面来演示从a路由组件切换到b路由组件,路由钩子函数的执行顺序(先排除更新路由钩子)
下面看看更新路由钩子
更新路由钩子函数触发条件是当组件复用时触发的,即对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候
下面我将a路由作为b路由的子路由来演示,b路由组件的生成和a路由组件更新,部分配置如下:
{ path: "/b", component: () => import("@/components/b.vue"), beforeEnter: (to, from, next) => { console.log("我是b的beforeEnter"); next(); }, children: [ { path: "/a/:id?",//配置参数 component: () => import("@/components/a.vue"), beforeEnter: (to, from, next) => { console.log("我是a的beforeEnter"); next(); }, }, ], },
总结下解析流程:
- 导航被触发
- 失活的组件调用离开守卫(beforeRouteLeave)
- 调用前置守卫(beforeEach)
- 在重新复用的组件调用beforeRouteUpdate守卫
- 在路由配置调用beforeEnter
- 解析异步路由组件
- 在被激活的组件调用beforeRouteEnter
- 调用全局beforeResolve守卫
- 导航被确认后调用afterEach后置守卫
- 触发DOM更新