环境
- Vue3、vue-router4、vite
问题
父组件使用局部
<router-view>
,用于渲染子组件,并且使用<keep-alive>
。同时需要局部页面刷新,所以加了一个if的判断语句、参照官网写法,结果会发现,路由跳转时、子组件渲染了两次,并且是在父组件挂载前就执行了,在子组件的onMounted钩子打印当前路由信息为上一个路由信息
<router-view v-slot="{ Component, route }" v-if="isRouterAlive">
<transition>
<keep-alive>
<suspense>
<template #default>
<component
:is="Component"
:key="route.meta.usePathKey ? route.path : undefined"
/>
</template>
<template #fallback> Loading... </template>
</suspense>
</keep-alive>
</transition>
</router-view>
分析一:
- 在vue-router的github上查找相关问题,发现vue-router和keep-alive一起使用,是有可能出现子组件渲染两次的问题,并且这个问题只有vue3才出现,vue2暂时为出现这个问题。自己摸索了很久,结果发现是官网bug的时候就感觉很无措。
- 但我发现keep-alive缓存也失效了,考虑是不是因为不同路由(动态路由)渲染同一个子组件的问题,导致keep-alive失效
通过了解keep-alive的工作原理,发现并非是动态路由引起的,因为keep-alive是缓存路由名称来缓存匹配的组件,如果路由名称不变,是不会更新dom的,并且缓存之后的组件,不在执行onMounted钩子,只有activated,deactivated钩子函数,我在这两函数打印发现能够执行,那就证明keep-alive有起作用- 为了验证并不是keep-alive的问题,写了个demo测试
目录
layout.vue
___fatherOne.vue
_______childOne.vue
_______childchild.vue
___fatherTwo.vue
_______childTwo.vue
_______childchild.vue
路由
import {
createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path:'/',
name: '首页',
component:() => import('../views/layout.vue'),
children:[
{
name: 'fartherone',
path: '/',
component: () =>
import('../views/fatherOne.vue')
},
{
name: 'farthertwo',
path: '/fartherTwo/:id',
component: () =>
import('../views/fatherTwo.vue'),
},
]
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
fatherOne.vue
<template>
<h1>
fatherOne
</h1>
<child-one></child-one>
<button @click="goToFathTWO">goToFathTWO</button>
</template>
<script>
import childOne from './childOne.vue';
import {
onMounted } from 'vue';
import {
useRoute,useRouter } from 'vue-router'
export default {
components: {
childOne },
setup(){
const router = useRouter()
const goToFathTWO = () =>{
router.push('/fartherTwo')
}
onMounted(()=>{
const route = useRoute()
console.log('fatherOne',route.name);
})
return{
goToFathTWO,
}
},
}
</script>
<style>
</style>
fatherTwo.vue
<template>
<h1>
fatherTwo
</h1>
<child-two></child-two>
<button @click="goToFathOne">goToFathOne</button>
</template>
<script>
import childTwo from './childTwo.vue';
import childTwoVue from './childTwo.vue';
import {
onMounted } from 'vue';
import {
useRoute,useRouter } from 'vue-router'
export default {
components: {
childTwo },
setup(){
const router = useRouter()
const goToFathOne = () =>{
router.push('/')
}
onMounted(()=>{
const route = useRoute()
console.log('fatherTWO',route.name);
})
return{
goToFathOne,
}
},
}
</script>
<style>
</style>
childOne.vue
<template>
<h1>
childOne
</h1>
<childchildVue></childchildVue>
</template>
<script>
import {
onMounted } from 'vue';
import {
useRoute,useRouter } from 'vue-router'
import childchildVue from './childchild.vue';
export default {
components: {
childchildVue },
setup(){
const route = useRoute
// console.log('route--childONe',route.name);
onMounted(()=>{
console.log('route--childONe',route.name);
})
}
}
</script>
<style>
</style>
childTwo.vue
<template>
<h1>
childTwo
</h1>
<childchildVue></childchildVue>
</template>
<script>
import {
onMounted } from 'vue';
import {
useRoute,useRouter } from 'vue-router'
import childchildVue from './childchild.vue';
export default {
components: {
childchildVue },
setup(){
const route = useRoute
// console.log('route--childTwo',route.name);
onMounted(()=>{
console.log('route--childTwo',route.name);
})
}
}
</script>
<style>
</style>
childchild.vue
<template>
<h1>
childchild
</h1>
</template>
<script>
import {
onMounted } from 'vue';
import {
useRoute,useRouter } from 'vue-router'
export default {
setup(){
const route = useRoute
// console.log('route--childchild',route.name);
onMounted(()=>{
console.log('route--childchil',route.name);
})
}
}
</script>
<style>
</style>
输出结果
首次进入
点击按钮goToFathTWO
点击按钮btn1-2,无输出
那大概不是router-view与keep-alive一起使用所引起的问题
分析二
排除router-view与keep-alive的藕合问题(可能存在)
那就锁定 在v-if和key值绑定
先去除:key,
- 确实减少了渲染次数,但是还是存在一个问题,keep-alive缓存失效,
上网查了keep-alive的缓存失效的问题,
- 组件没有name属性
- 路由的name属性跟组件的name属性不一致
- 动态绑定key
以上问题我都有遵循,但还是没有缓存
分析三
只剩下if,
去除if之后,keep-alive就能够缓存了
这时候我才恍惚,因为if是对dom的动态创建和销毁,把if判断在<router-view>,就会使router-view的路由跳转的时候就会创建和销毁,定然keep-alive会失效,每次跳转都会重新渲染,再加上我为了解决不同路由同组件,内容没更新的问题再动态绑定key值导致,再一次重新渲染刷新,就一共渲染了两次
补充
使用v-if来局部刷新页面
是目前网上不叫推荐的一种做法,能够提高用户体验感,减缓空白页出现
const reload = () => {
isRouterAlive.value = false;
nextTick(function () {
isRouterAlive.value = true;
});
};
但会使keep-alive失效
所以采用一下方法可以让数据更新
动态路由
如何参数是从路径获取、
使用官方 onBeforteRouteUpdate
组合式api onBeforteRouteUpdate(to,from)
onBeforteRouteUpdate((to,from) => {
)
watch监听
监听路由变化
watch(
() => route,
(newVal,oldVal) =>{
}
)
computed
const params = computed(
() = > return route.params
)
动态绑定key
<component
:is="Component"
:key="route.meta.usePathKey ? route.path : undefined"
/>