带路由器的可滑动 Vuetify 选项卡

Posted

技术标签:

【中文标题】带路由器的可滑动 Vuetify 选项卡【英文标题】:Swipable Vuetify Tabs with router 【发布时间】:2020-09-28 06:58:58 【问题描述】:

在我的 vue 应用程序中,我有一个包含一些选项卡的页面。 我想根据不同的路线更改/显示选项卡。

为此,我使用了这个answer 作为参考。

总的来说,这工作正常!我什至可以通过在移动设备上滑动来更改标签(感谢v-tabs-items 上的@change 侦听器。

但是:单击选项卡标签时,<router-view> 加载的组件被安装了两次。滑动时,只安装一次。

原因与 <router-view> 位于 <v-tab-item>s 的循环内有关。

如果我把它放在这个循环之外,子组件会被正确安装一次。 不幸的是,我不能再使用滑动来更改选项卡,因为内容是解耦的。

所以:是否有机会同时拥有这两种功能(动态路由内容可滑动性)?

谢谢大家!


Vue:

<template>
 <!-- [...] -->
 <v-tabs centered="centered" grow v-model="activeTab">
    <v-tab v-for="tab of tabs" :key="tab.id" :id="tab.id" :to="tab.route" exact>
      <v-icon> tab.icon </v-icon>
    </v-tab>

    <v-tabs-items v-model="activeTab" @change="updateRouter($event)">
      <v-tab-item v-for="tab of tabs" :key="tab.id" :value="resolvePath(tab.route)" class="tab_content">
        <!-- prevent loading multiple route-view instances -->
        <router-view v-if="tab.route === activeTab" />
      </v-tab-item>
    </v-tabs-items>
  </v-tabs>
  <!-- [...] -->
</template>

<script lang="ts">
data: () => (
    activeTab: '',
    tabs: [
      id: 'profile', icon: 'mdi-account', route: '/social/profile',
      id: 'friends', icon: 'mdi-account-group', route: '/social/friends',
      id: 'settings', icon: 'mdi-cogs', route: '/social/settings',
    ]
  ),
  methods: 
    updateRouter(tab:string) 
      this.$router.push(tab)
    
  ,
</script>

路由器:


    path: "/social",
    component: () => import("../views/Social.vue"),
    meta: 
      requiresAuth: true
    ,
    children: [
      
        path: "profile",
        component: () => import("@/components/social/Profile.vue")
      ,
      
        path: "friends",
        component: () => import("@/components/social/Friendlist.vue")
      ,
      
        path: "settings",
        component: () => import("@/components/social/ProfileSettings.vue")
      
    ]
  

【问题讨论】:

【参考方案1】:

这种行为可以描述事情发生的顺序吗? @change 更新路由发布activeTab,点击标签更新路由然后activeTab?因此路由器视图在标签视图更新之前位于下一个视图中,因此它在同一路径上显示了两个不同的路由器视图。

要解决这个问题,只需更改

<router-view v-if="tab.route === activeTab" />

<router-view v-if="tab.route === $route.fullPath && tab.route === activeTab" />

<router-view v-if="tab.route === $route.path && tab.route === activeTab" />

【讨论】:

是的!我也观察到了这种行为,但无法完成最后一步!扩展检查以包括 $route.path 完成了这项工作! 在我的情况下,我无法使用这个额外的检查,因为我有一些选项卡上有更多的子路由,所以 $route.path 不匹配。我发现这两个挂载调用来自:1)“旧” 正在填充新组件,因为路由发生了变化。但是选项卡尚未更新,因此新组件呈现在错误的选项卡中。 2)标签被更新,所以现在新组件被渲染到它自己的标签中。而且因为我们使用 v-if 来控制 插槽的存在,它会被销毁并再次创建。【参考方案2】:

我有不同的答案要提供。 @Estradiaz 的答案对我不起作用,因为我的一些选项卡中有子视图,并且从选项卡重定向到该选项卡上的第一个子视图。最终结果是我遇到了很多 $route.path 与 tab.route 不匹配的情况,因为 tab.route 是选项卡的路由,而 $route.path 指向了该选项卡的其中一个子选项。

我确实找到了一种我认为更好且更普遍适用的替代解决方案。但在我开始讨论之前,我想充分解释发生了什么。

因为我们使用 :to 属性,所以我们让路由器处理组件的所有选择和显示。这意味着在基础设施有机会做出反应之前,路线就会发生变化。例如,如果我们有 2 个选项卡,而我正在显示选项卡 1,那么显示的是这个(你可以在你的 html 中看到这个)

<v-tabs-items>
  <v-tab-item><router-view>tab 1 component</router-view></v-tab-item>
  <--- tab 2 commented out --->
</v-tabs-items>

那么当我们点击标签2时,会发生这样的事情:

<v-tabs-items>
  <v-tab-item><router-view>tab 2 component</router-view></v-tab-item>
  <--- tab 2 commented out --->
</v-tabs-items>

然后过了一段时间,代码开始更新,你过渡到这个:

<v-tabs-items>
  <--- tab 1 commented out --->
  <v-tab-item><router-view>tab 2 component</router-view></v-tab-item>
</v-tabs-items>

但是由于 v-if 发生了变化,我们正在销毁 for tab 1 以及新的 tab 2 组件,只是为了让 for tab 2 创建 tab 2 组件的新实例。

基础架构上似乎没有任何东西可以用来知道显示的选项卡和其中的组件不匹配。 @Estradiaz 的解决方案对于简单的标签组件来说是不错的,但它并不可靠。

我没有尝试阻止不匹配的情况,而是使用此解决方案来接受它,并使用 keep-alive 基础设施来“重用”当我们吹走 tab 1's 时。 以下是问题中给出的原始标记的样子:

<template>
 <!-- [...] -->
 <v-tabs centered="centered" grow v-model="activeTab">
    <v-tab v-for="tab of tabs" :key="tab.id" :id="tab.id" :to="tab.route" exact>
      <v-icon> tab.icon </v-icon>
    </v-tab>

    <v-tabs-items v-model="activeTab" @change="updateRouter($event)">
      <v-tab-item v-for="tab of tabs" :key="tab.id" :value="resolvePath(tab.route)" class="tab_content">
        <div v-if="tab.route === activeTab">
          <keep-alive>
            <router-view key="1"  />
          </keep-alive>
        </div>
      </v-tab-item>
    </v-tabs-items>
  </v-tabs>
  <!-- [...] -->
</template>

现在,当您单击选项卡 2 时会发生以下情况:

<v-tabs-items>
  <v-tab-item><router-view>tab 2 component</router-view></v-tab-item>
  <--- tab 2 commented out --->
</v-tabs-items>

并且路由器视图现在创建选项卡 2 组件并显示它。一段时间过去了,v-tab 基础架构更新,标记也发生了变化:

<v-tabs-items>
  <--- tab 1 commented out --->
  <v-tab-item><router-view>tab 2 component</router-view></v-tab-item>
</v-tabs-items>

但是现在当从 dom 中删除原始文件时,它并没有被删除,而是被缓存了。并且当新的被创建时,它来自准备好与已经构建的选项卡 2 组件一起使用的缓存。

就我而言,这完美地消除了正在发生的双重安装。而且它可能会快一点,因为它不必在每次切换时都重新创建。

【讨论】:

以上是关于带路由器的可滑动 Vuetify 选项卡的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式更改 Vuetify 选项卡和 vue-router

使用路由或查询更改 vuetify 选项卡

如何通过路由器名称将 Vuetify 选项卡与 vue-router 一起使用

滑块菜单片段中的可交换选项卡

TabTopAutoLayout自定义顶部选项卡区域(带下划线)(动态选项卡数据且可滑动)

防止JavaFX TabPane在滑动时切换选项卡