关于keep-alive生命周期钩子
在开发Vue项目的时候,大部分组件是没必要多次渲染的,所以Vue提供了一个内置组件keep-alive来缓存组件内部状态,避免重新渲染,

在开发Vue项目的时候,大部分组件是没必要多次渲染的,所以Vue提供了一个内置组件keep-alive来缓存组件内部状态,避免重新渲染,
是一个抽象组件:它自身不会渲染一个 DOM 元素,也不会出现在父组件链中
keep-alive
在被keep-alive包含的组件/路由中,会多出两个生命周期的钩子:activated 与 deactivated
activated:在组件第一次渲染时会被调用,之后在每次缓存组件被激活时调用
deactivated:组件被停用(离开路由)时调用
实际列子
html
<template>
<div>
<router-link to="/login" class="default-styles">Login</router-link><br>
<router-link :to="{ path: '/about' }" class="default-styles">About</router-link><br>
<router-link :to="{ path: '/user'}" class="default-styles">User</router-link><br>
<!--缓存user,login-->
<keep-alive include="user,login">
<router-view></router-view>
</keep-alive>
</div>
</template>
include:name 匹配的 路由/组件 会被缓存
exclude:name 匹配的 路由/组件 不会被缓存
exclude的优先级大于include,也就是include和exclude同时存在时,exclude生效,include不生效
include和exclude支持三种方式来有条件的缓存路由:采用逗号分隔的字符串形式,正则形式,数组形式,
正则和数组形式,必须采用v-bind形式来使用
使用了keep-alive就不会调用beforeDestroy(组件销毁前钩子)和destroyed(组件销毁),因为组件没被销毁,被缓存起来了
所以在被keep-alive包含的组件/路由中,会多出两个生命周期的钩子:activated 与 deactivated
activated: 在组件第一次渲染时会被调用,之后在每次缓存组件被激活时调用
deactivated: 组件被停用(离开路由)时调用
注意 这是组件的name值,不是导航的name值
main.js
js
const router = new VueRouter({
mode: 'hash',
routes: [{
path: '/user',
component: User,
name: 'user',
beforeEnter: function guardRoute(to, from, next) {
console.log('user导航路由独享守卫:beforeEnter')
next()
}
},
{
path: '/about',
component: About,
name: 'about',
beforeEnter: function guardRoute(to, from, next) {
console.log('about导航路由独享守卫:beforeEnter')
next()
}
},
{
path: '/login',
component: Login,
name: 'login',
beforeEnter: function guardRoute(to, from, next) {
console.log('login导航路由独享守卫:beforeEnter')
next()
}
}
]
})
router.beforeEach((to, from, next) => {
console.log('全局前置守卫: beforeEach')
next()
})
router.beforeResolve((to, from, next) => {
console.log('全局解析守卫: beforeResolve')
next()
})
router.afterEach((to, from) => {
console.log('全局后置钩子: afterEach')
})
Vue.use(VueRouter)
new Vue({
router,
render: h => h(App),
}).$mount('#app')
login组件
html
<template>
<div class="login">
这是login
</div>
</template>
<script>
export default {
name:'login',
beforeCreate(){
console.log('login组件beforeCreate')
},
created(){
console.log('login组件created')
},
beforeMount(){
console.log('login组件beforeMount')
},
mounted(){
console.log('login组件mounted')
},
activated(){
console.log('login组件activated')
},
deactivated(){
console.log('login组件deactivated')
},
beforeRouteEnter(to, from, next) {
console.log('login组件路由内守卫进入前:beforeRouteEnter')
next()
},
beforeRouteUpdate(to, from, next) {
console.log('login组件路由内守卫路由改变时:beforeRouteUpdate')
next()
},
beforeRouteLeave(to, from, next) {
console.log('login组件路由内守卫离开前:beforeRouteLeave')
next()
},
beforeDestroy(){
console.log('login组件beforeDestroy')
},
destroyed(){
console.log('login组件destroyed')
}
}
</script>
about组件
html
<template>
<div class="about">
about
</div>
</template>
<script>
export default {
name:'about',
beforeCreate(){
console.log('about组件beforeCreate')
},
created(){
console.log('about组件created')
},
beforeMount(){
console.log('about组件beforeMount')
},
mounted(){
console.log('about组件mounted')
},
activated(){
console.log('about组件activated')
},
deactivated(){
console.log('about组件deactivated')
},
beforeRouteEnter(to, from, next) {
console.log('about组件路由内守卫进入前:beforeRouteEnter')
next()
},
beforeRouteUpdate(to, from, next) {
console.log('about组件路由内守卫路由改变时:beforeRouteUpdate')
next()
},
beforeRouteLeave(to, from, next) {
console.log('about组件路由内守卫离开前:beforeRouteLeave')
next()
},
beforeDestroy(){
console.log('about组件beforeDestroy')
},
destroyed(){
console.log('about组件destroyed')
}
}
</script>
<style lang="less" scoped>
</style>
user组件
html
<template>
<div class="user">
user
</div>
</template>
<script>
export default {
name:'user',
beforeCreate(){
console.log('user组件beforeCreate')
},
created(){
console.log('user组件created')
},
beforeMount(){
console.log('user组件beforeMount')
},
mounted(){
console.log('user组件mounted')
},
activated(){
console.log('user组件activated')
},
deactivated(){
console.log('user组件deactivated')
},
beforeRouteEnter(to, from, next) {
console.log('user组件路由内守卫进入前:beforeRouteEnter')
next()
},
beforeRouteUpdate(to, from, next) {
console.log('user组件路由内守卫路由改变时:beforeRouteUpdate')
next()
},
beforeRouteLeave(to, from, next) {
console.log('user组件路由内守卫离开前:beforeRouteLeave')
next()
},
beforeDestroy(){
console.log('user组件beforeDestroy')
},
destroyed(){
console.log('user组件destroyed')
}
}
</script>
执行顺序
刷新浏览器,在这里我不分析其他钩子函数,如需要了解其他钩子函数可以看《你在使用vue-router时可能需要注意到的事项》这篇文章
1、然后点击第一次进入login组件,可以看到login组件执行了activated函数,说明组件被缓存

2、当从login组件跳转到about组件(该组件不是缓存组件),可以看到login组件执行了deactivated函数,说明组件被停用了

3、从about组件跳转到user组件,发现about执行了beforeDestroy和destroyed销毁前后函数
user是缓存组件,并且也执行了activated函数,组件已被缓存

4、从user组件跳转到login组件,发现login的 beforeCreate created beforeMount mounted 钩子函数均为执行,但是执行了login组件的activated函数
所以你要是把数据请求挂载放到了created beforeMount mounted函数中是不会执行的,如果你即想缓存组件,又想调用某个接口,可以把ajax放在activated中

总结
将使用vue-router、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:组件生命周期,生成html
10、deactivated: 离开缓存组件A,没有缓存的话者触发A的beforeDestroy和destroyed组件销毁钩子。
11、mounted:访问/操作DOM。
12、activated:进入缓存组件,如果A中含有子组件会执行,会先执行子组件的activated,在执行父组件的activated