项目笔记第三篇-router-view的key

335 阅读2分钟

前言

我在愉快的切图中遇到了一个问题,简单的问题排查了一上午,在这里记录一下。 图片.png

点击菜单-组织架构图,此时路由发生变化。菜单滚动条居然直接滚到了顶部。

图片.png

首先说一下大概的界面布局,整个界面是父亲(father)界面,tab栏加界面内容是儿子(son)界面,界面内容是孙子界面(grandson)。以下给出伪代码说明嵌套结构:

//father.vue
<template>
    <!-- 树形结构菜单 -->
    <n-menu></n-menu>
    <!-- 子路由 -->
    <router-view v-slot="{ Component, route }">
        <keep-alive>
            <component :is="Component" v-if="route.meta.keepAlive" :key="route.path" />
        </keep-alive>
            <component :is="Component" v-if="!route.meta.keepAlive" :key="route.path" />
    </router-view>
<template/>
//son.vue
<template>
    <!-- tab切换栏 -->
    <n-tab></n-tab>
    <!-- 孙子路由,页面内容 -->
    <router-view v-slot="{ Component, route }">
        <keep-alive>
            <component :is="Component" v-if="route.meta.keepAlive" :key="route.path" />
        </keep-alive>
            <component :is="Component" v-if="!route.meta.keepAlive" :key="route.path" />
    </router-view>
<template/>

图片.png

问题

好了,进入正题。点击菜单,子路由发生改变。按正常情况来说,位于父亲界面的菜单滚动条位置是不会发生改变的,因为子界面共用了父亲界面的同一个菜单,vue-roter官方文档是这么说的。

图片.png

但实际上滚动条自己回到了初始位,考虑是不是父亲界面被重新被重新创建了。使用onMounted钩子测试一下,果然点击菜单切换子路由时,输出语句被执行了。

图片.png

解决

透过现象看本质,为什么会出现这种情况。先来看一段代码:

图片.png 这是笔者导致以上问题产生的代码。它跟下面的代码等价

 <keep-alive>
    <router-view v-slot="{ route }" v-if="route.meta.keepAlive" :key="route.path" />
 </keep-alive> 
 <router-view v-if="!route.meta.keepAlive" :key="route.path" />

我们来学习一下router-view组件key属性的作用:

  1. router-view不设置key或者路由key值一致的时候,vue会复用相同组件,比起创建再销毁,这显然更高效。但对于路由有多个子路由来说,当在子路由来回切换时,会导致页面不刷新的问题,这是因为不再执行created和mounted这些钩子函数,可以通过watch来监听route的变化从而加载不同的组件
  2. 设置key值为route.path,因为子路由的path不同,从而避免了组件复用,子路由间来回切换时,页面都会重新加载。

这样问题就很明显了,因为App中的路由入口router-view设置了path作为key,所以界面被重新加载了。解决方法也很简单,我们只需要去掉key就好了,这样点击切换子路由时,菜单就不会被重新加载。

图片.png

总结

给router-view设置key每次路由切换时都会重新加载界面,但这样切换子路由时可能会导致界面意外的重新加载、或是onMounted方法的重新执行,给用户造成不好的体验或是性能浪费。但不设置key也可能导致页面不刷新问题。这需要我们知道原理后灵活的设置。

参考

  1. # Vue嵌套路由导致父组件重复渲染BUG
  2. # <router-view>中key属性的作用