如何在 vue-router 中使用 Vuetify 选项卡

How to use Vuetify tabs with vue-router

我有以下 jsfiddle 有两个 Vuetify 选项卡。该文档没有显示使用 vue-router 的示例。

我发现这个 Medium.com post 关于如何将 Vuetify 与 vue-router 一起使用,它有以下代码:

<div id="app">
  <v-tabs grow light>
    <v-tabs-bar>
      <v-tabs-item href="/" router>
        <v-icon>motorcycle</v-icon>
      </v-tabs-item>
      <v-tabs-item href="/dog" router>
        <v-icon>pets</v-icon>
      </v-tabs-item>
    </v-tabs-bar>
  </v-tabs>

  <router-view />
</div>

但是,代码现在已经过时了,因为 Vuetify 1.0.13 Tabs documentation 没有在他们的 api 中指定 router 道具,所以 post 中的嵌入式示例没有没用。

我还找到了这个 ,它有以下代码:

<v-tabs-item :to="{path:'/path/to/somewhere'}">

然而,使用 to 道具不起作用,它也没有在 Vuetify api 中列出。相比之下,v-button Vuetify 组件确实列出了一个 to 道具并利用了 vue-router,所以我希望 vue-router 支持的组件能够支持 to 道具。

在旧的 old Vuetify 0.17 docs 中挖掘,to prop 是为 v-tabs-item 指定的。似乎在 1.0.13 中可能已删除支持。

如何将 vue-router 与 Vuetify 选项卡一起使用?

更新

天哪!我要求 Vuetify 社区向他们的 api 添加文档,看起来他们只是向 Vuetify tabs docs 添加了 to 道具以及其他 vue-router 道具。说真的,那里的社区很棒。

原答案

Vuetify 社区 Discord 中的人们能够帮助我。我更新后的 jsfiddle 现在有了工作代码。

本质上,v-tabrouter-link 的包装器,我假设它使用插槽将道具传递给 router-link,因此将 to 道具放在 v-tab 工作正常。

以下代码是工作代码的示例:

html

<v-app dark>
  <v-tabs fixed-tabs>
    <v-tab to="/foo">Foo</v-tab>
    <v-tab to="/bar">Bar</v-tab>
  </v-tabs>
  <router-view></router-view>
</v-app>

js

const Foo = {
  template: '<div>Foo component!</div>'
};

const Bar = {
  template: '<div>Bar component!</div>'
};

const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar },
];

const router = new VueRouter({ routes });

new Vue({
  el: '#app',
  router,
});

结果

我只是在此处添加一些与动画相关的提示并阐明 v-tab-itemsv-tab-item 的用法。

如果您已经注意到,使用如下工作标记会阻止 v-tabs 选项卡切换动画工作:

<v-tabs v-model="activeTab">
  <v-tab key="tab-1" to="/tab-url-1">tab 1</v-tab>
  <v-tab key="tab-2" to="/tab-url-2">tab 2</v-tab>
</v-tabs>
<router-view />

如果你想保留标签切换动画,这是一种方法。

<v-tabs v-model="activeTab">
  <v-tab key="tab-1" to="/tab-url-1" ripple>
    tab 1
  </v-tab>
  <v-tab key="tab-2" to="/tab-url-2" ripple>
    tab 2
  </v-tab>

  <v-tab-item id="/tab-url-1">
    <router-view v-if="activeTab === '/tab-url-1'" />
  </v-tab-item>
  <v-tab-item id="/tab-url-2">
    <router-view v-if="activeTab === '/tab-url-2'" />
  </v-tab-item>
</v-tabs>

Note that you can also use a v-for loop on your v-tab and v-tab-item tags as long as your to value is found among the id attribute of your v-tab-items.

If you need to place your tab contents in a different place than your tabs buttons, this is what v-tab-items is for. You can place the v-tab-items in a v-tab-items tag outside of the v-tabs component. Make sure you give it a v-model="activeTab" attribute.

模板部分:

<div>
    <v-tabs
      class="tabs"
      centered
      grow
      height="60px"
      v-model="activeTab"
    >
      <v-tab v-for="tab in tabs" :key="tab.id" :to="tab.route" exact>
        {{ tab.name }}
      </v-tab>
    </v-tabs>
    <router-view></router-view>
</div>

和js部分:

  data() {
    return {
      activeTab: `/user/${this.id}`,
      tabs: [
        { id: 1, name: "Task", route: `/user/${this.id}` },
        { id: 2, name: "Project", route: `/user/${this.id}/project` }
      ]
    };
  }

路线:

{
  path: "/user/:id",
  component: User1,
  props: true,
  children: [
    {
      path: "", //selected tab by default
      component: TaskTab
    },
    {
      path: "project",
      component: ProjectTab
    }
  ]
}

See codesanbox example

@roli-roli 和@antoni 的答案是正确的,但缺少一个微小的细节。事实上,使用他们的方法(几乎等效)在移动视图中存在问题;事实上,在这种情况下,选项卡变得可滑动。问题是滑动不会按预期更新路线,从带有组件 A 的选项卡 A 传递到带有组件 B 的选项卡 B;相反,将触发动画并且 activeTab 将更改,但路由器不会更新。

TL;DR 滑动 v-tab-item 不会更新路由器,并且您在每个选项卡下得到相同的组件。

解决方案可以是禁用 v-tab-item 的可滑动性或监听 tab 组件的更改事件以相应地更新路由器。我会建议第二种解决方案(因为在某些情况下滑动结果非常方便),但我认为这会在单击选项卡标签时触发两次路由器更新。

这是一个基于@roli-roli 回答的工作示例

模板

<template>
    <v-tabs v-model="activeTab">
        <v-tab v-for="tab in tabs" :key="tab.id" :to="tab.route">{{ tab.name }}</v-tab>

        <v-tabs-items v-model="activeTab" @change="updateRouter($event)">
            <v-tab-item v-for="tab in tabs" :key="tab.id" :to="tab.route">
                <router-view />          
            </v-tab-item>
        </v-tabs-items>
    </v-tabs>
</template>

脚本

export default {
    data: () => ({
        activeTab: '',
        tabs: [
            {id: '1', name: 'Tab A', route: 'component-a'},
            {id: '2', name: 'Tab B', route: 'component-b'}
        ]
    }),
    methods: {
        updateRouter(val){
            this.$router.push(val)
        }
    }
}

路由器

按照之前的答案进行设置。

以下代码适合我

  <v-tabs fixed-tabs>
          <v-tab>Locations</v-tab>
          <v-tab>Employees</v-tab>
          <v-tab-item>Tab 1 content
            <locations-data/>
          </v-tab-item>
          <v-tab-item>Tab 2 content
            <employees-data/>
          </v-tab-item>
    </v-tabs>
   <script>
        import LocationsData from './Settings/Locations';
        import EmployeesData from './Settings/Employees';
        export default {
           components: {
            LocationsData,
            EmployeesData
          },
        }
   </script>
//urls in some componet
<v-btn @click="goToUrl({name: 'RouteName1'})">
    .....
<v-list-item-title 
    @click="goToUrl({name: 'RouteName2'})"
>
    Some tab link text
 </v-list-item-title>
    ....


//tabs menu and content in other component
<v-tabs>
    <v-tab key="key_name1" :to="{ name: 'RouteName1'}">Some link 1</v-tab>
    <v-tab key="key_name2" :to="{ name: 'RouteName2'}">Some link 2</v-tab>

<v-tab-item key="key_name1" value="/url/for/route1">
    ....
<v-tab-item key="key_name2" value="/url/for/route2">
    ....

启用动画和滑动

并保留您现有的查询参数:有用的,例如如果您的 url:

中有 :locale

模板

<v-tabs v-model="activeTab">
   <v-tab v-for="tab in tabs" :key="tab.id" :to="tab.to">
    {{ tab.name }}
  </v-tab>
</v-tabs>

<v-tabs-items v-model="activeTab" @change="updateRouter($event)">
  <v-tab-item v-for="tab in tabs" :key="tab.id" :value="tab.to">
    <router-view />          
  </v-tab-item>
</v-tabs-items>

脚本

export default {
  data: () => ({
    activeTab: '',
  }),
  computed: {
    tabs() {
      return [
        { id: 1, name: 'Tab one', to: this.getTabPath('routeName1') },
        { id: 2, name: 'Tab two', to: this.getTabPath('routeName2') },
        { id: 3, name: 'Tab three', to: this.getTabPath('routeName3') },
        { id: 4, name: 'Tab four', to: this.getTabPath('routeName4') },
      ]
    },
  },
  methods: {
    getTabPath(name) {
      // Get the path without losing params. Usefull e.g. if you have a :locale in your url:
      return this.$router.resolve({ name, params: this.$route.params }).href
    },
    updateRouter(path) {
      // Gets called upon swipe.
      this.$router.push(path)
    },
  },
}

使用 $router.resolve 从路由对象获取路径,如 所述。

我只想添加一个修复程序,用于在切换到选项卡时双重安装新组件。 可以看到问答.

TLDR 是,如果您在 中使用 v-for,您将 运行 遇到一个问题,即您要切换到的组件将在每次切换时创建、销毁然后再次创建.如果像我一样,您在创建端执行 ajax 调用,那么您将在每次标签切换时点击后端两次。您可以用 a 包裹起来以防止出现这种情况。

在回答中我深入探讨了为什么会发生这种情况。