KeepAlive和携带key属性的RouteView同时使用,routerView初始没有匹配路由,手动跳转页面为什么渲染失败?

新手上路,请多包涵

页面首次加载,加载了一个包含<router-view/>标签的页面,且被keepAlive包裹,如下

//index.vue ,路由为/index
<div>刷新按钮</div>
<div>打开a页面</div>
<keep-alive>
  <router-view/>
</keep-alive>

此时<router-view>是没有渲染内容的,因为路由只匹配到当前页面层,但是在后续操作中,想通过刷新按钮,使得keepAlive包裹下的所有页面能手动刷新,所以我给router-view增加了key。
出现了一个新的问题,当想要打开a页面(路由/index/a)的时候,通过$router.push()方法去跳转时,总是无法渲染a页面,而再去点击菜单其他选项跳转页面的时候,是可以成功的。只有第一个页面总是无法渲染成功,请问是什么原因呢?

当去掉KeepAlive的时候,页面能够成功渲染。
我的路由配置

{
  path: '/index',
  name: 'index',
  component:() => import('@/pages/index/index'),
  children:[
    {
        path: "a",
        name: "a",
        component: () => import('@/pages/index/a')
    }
    ......
  ]
},
阅读 273
avatarAI BotBETA

回答

问题出在 keep-alive 和动态路由(带有子路由的 router-view)的结合使用上。keep-alive 缓存了组件的状态,但在某些情况下,尤其是当路由路径变化而组件未正确销毁和重建时,可能会导致渲染问题。

原因分析

  1. 初始状态无匹配路由
    当页面首次加载 /index 时,<router-view/> 内没有匹配的子路由,因此没有渲染任何内容。此时 keep-alive 包裹了一个空的 router-view
  2. 动态添加 key 属性
    虽然你尝试通过添加 key 属性来强制组件重新渲染,但如果 key 值的变化没有正确触发组件的销毁和重建,keep-alive 仍然会保持之前的状态(即空状态)。
  3. 路由跳转问题
    当你通过 $router.push() 跳转到 /index/a 时,如果 keep-alive 缓存了之前空状态的 router-view,它可能不会正确地更新为新路由对应的组件。

解决方案

  1. 确保 key 变化有效
    确保每次路由变化时,key 属性都能有效变化。这可以通过监听路由变化并动态设置 key 来实现。例如:

    <template>
      <div>
        <div>刷新按钮</div>
        <div>打开a页面</div>
        <keep-alive :include="cachedViews">
          <router-view :key="routeKey"/>
        </keep-alive>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          routeKey: this.$route.fullPath, // 使用完整路径作为key
          cachedViews: ['index', 'a'] // 根据需要缓存的视图设置
        };
      },
      watch: {
        $route(to, from) {
          // 当路由变化时,更新key
          this.routeKey = to.fullPath;
        }
      }
    };
    </script>
  2. 条件渲染 router-view
    如果可能,考虑在路由有子路由时才渲染 router-view,这样可以避免 keep-alive 缓存一个空视图。
  3. 调整路由配置
    确保路由配置正确,并且所有需要的组件都已正确导入和注册。
  4. 使用 v-if 控制 keep-alive
    在某些情况下,使用 v-if 根据条件渲染 keep-aliverouter-view 可能更可靠:

    <template>
      <div>
        <div>刷新按钮</div>
        <div>打开a页面</div>
        <keep-alive v-if="hasChildRoute" :include="cachedViews">
          <router-view :key="routeKey"/>
        </keep-alive>
        <router-view v-else :key="routeKey"/>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          routeKey: this.$route.fullPath,
          cachedViews: ['index', 'a'],
          hasChildRoute: false
        };
      },
      watch: {
        $route(to) {
          this.routeKey = to.fullPath;
          this.hasChildRoute = to.matched.some(record => record.children && record.children.length);
        }
      },
      created() {
        this.hasChildRoute = this.$route.matched.some(record => record.children && record.children.length);
      }
    };
    </script>

通过这些方法,你可以更可靠地控制 keep-aliverouter-view 的行为,确保在路由变化时正确渲染组件。

1 个回答

KeepAlive 和 key 属性的结合使用导致了 RouterView 的缓存行为与路由更新的不匹配。在首次加载 /index 页面时,KeepAlive 包裹的 RouterView 没有渲染内容(因为没有匹配到子路由),而当你通过 $router.push() 跳转到 /index/a 时,KeepAlive 的缓存机制可能无法正确处理子路由的变化

<div>
    <div @click="refresh">刷新按钮</div>
    <div @click="goToA">打开a页面</div>
    <keep-alive>
      <router-view :key="routerViewKey" />
    </keep-alive>
  </div>
 const routerViewKey = ref(route.fullPath)

加个key应该就好了吧

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏