如何使用Vue-router与Vuetify选项卡

80

我有以下jsfiddle,其中包含两个Vuetify选项卡。文档中没有展示如何使用vue-router的示例。

我在Medium.com帖子中找到了关于如何使用vue-router与Vuetify的方法,其中包含以下代码:

<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属性,因此该代码现已过时,因此文章中的嵌入式示例无法工作。
我还发现了这个StackOverflow answer,其中包含以下代码:
<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属性被指定为v-tabs-item。似乎在1.0.13中已经删除了对它的支持。
如何在Vuetify标签页中使用vue-router
8个回答

97

更新

哇塞!我请求 Vuetify 社区为他们的 api 添加文档,看起来他们刚刚在 Vuetify tabs 文档中添加了 to 属性以及其他 vue-router 属性。真的,那里的社区太棒了。

原始回答

Vuetify 社区 Discord 中的朋友能够帮助我。我的更新后的 jsfiddle 现在有了可用的代码。

实际上,v-tabrouter-link 的包装器,我假设它使用插槽将属性传递给 router-link,因此在 v-tab 上放置 to 属性可以正常工作。

以下代码是可用代码的示例:

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>

JavaScript

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,
});

结果

Foo tab example Bar tab example


谢谢!我已经创建了一个分支来添加一个默认路由到选项卡,如果有人感兴趣的话:https://jsfiddle.net/thorne51/9g2zeow1/2/ - thorne51
如果您想将属性传递给组件怎么办? - George Katsanos
1
问题在于让动画在路由之间正常工作... - Douglas Gaskell

27

模板部分:

<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>

并且 JavaScript 部分:

  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
    }
  ]
}

查看codesanbox示例


我看到的问题是,当尝试这样做时,它最终会加载每个页面两次。如果你观察 mounted(),你会发现在使用选项卡进行路由时,它会加载页面两次。需要找到解决方法。https://codesandbox.io/s/vuetify-v-tabs-with-vue-router-in1le 观察控制台中的 "build loaded",应该只执行一次,但实际上会挂载两次。 - tmarois
我明白了。我注意到你正在运行路由视图循环。你不应该在选项卡数组中运行路由视图。这会导致它加载两次。 - tmarois
2
@timothymarois 你说得完全正确。我更新了我的答案,现在应该可以按预期工作了。 - Roland
如果组件需要从父组件传递属性怎么办? - George Katsanos
为什么需要 props?如果 props 与路由有关,您可以通过路由传递 props。 - Roland

17

我在这里添加一些有关动画的提示,并澄清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>
请注意,只要您的“to”值在您的“v-tab-item”标签的“id”属性中找到,即可在您的“v-tab”和“v-tab-item”标记上使用“v-for”循环。
如果您需要将选项卡内容放置在与选项卡按钮不同的位置,则可以使用“v-tab-items”。您可以在“v-tabs”组件之外的“v-tab-items”标记中放置“v-tab-item”。确保给它一个“v-model =“ activeTab””属性。

4

拥有动画和滑动功能

并保留您现有的查询参数:如果您的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 可以从路由对象中获取路径,如此处所述。


2
@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>

Script

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)
        }
    }
}

Router

Set up as in previous answers.


1
请使用:value="tab.route"代替:to和<router-view>。谢谢! - Stevie-Ray Hartog
@Stevie-RayHartog 是的,我纠正了那个写错的 router-view。你为什么建议使用 :value 而不是 :to? - Gabriel Rambaud
要禁用选项卡滑动,只需使用 touchless 属性,像这样 <v-tabs-items v-model="..." touchless> - rustyx
我在使用对象作为“to”值时遇到了问题,而不是字符串。似乎vuetify会将路由解析为字符串,并将v-model设置为该字符串,因此将其解析为字符串以用作v-tab-item值将允许正确引用它。 https://codesandbox.io/s/v-tab-to-7jteu - discomatt
我不知道为什么,但只有当我在选项卡项目上将“to”更改为“value”时,它才对我起作用。 - EuberDeveloper
显示剩余3条评论

0
//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">
    ....

0

我只想为切换选项卡时新组件的双重挂载添加修复。

您可以在这里查看问题和答案。

简而言之,如果您在<keep-alive>中使用v-for,则会遇到一个问题,即您要切换到的组件将在每次切换时创建、销毁,然后再次创建。如果像我一样,在创建端执行ajax调用,则每次切换选项卡都会向后端发送两次请求。您可以使用<transition>包装<keep-alive>以防止出现此问题。

在答案中,我详细解释了为什么会发生这种情况。


-1

以下代码对我有效

  <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>

第一次在选项卡下面出现了拉伸的线,如何解决这个问题。 - Atchutha rama reddy Karri
我以前用过你的方法,如果我没记错的话,它会在主组件挂载时加载每个子组件。而使用 :toto 属性则只在点击选项卡时加载相应的组件。在我看来,这种方式更好。 - lbris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接