【问题标题】:How to use Vuetify tabs with vue-router如何在 vue-router 中使用 Vuetify 选项卡
【发布时间】:2018-09-18 04:32:09
【问题描述】:

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

我在vue-router 中找到了这个Medium.com post,它有以下代码:

<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

【问题讨论】:

    标签: javascript vue.js vuejs2 vuetify.js


    【解决方案1】:

    我只想为切换到选项卡时新组件的双重安装添加一个修复程序。 可以看问答here

    TLDR 是,如果您在 中使用 v-for,您将遇到一个问题,即您要切换到的组件将被创建、销毁,然后在每次切换时再次创建。如果像我一样,您在其创建端进行 ajax 调用,那么每次选项卡切换都会敲击后端两次。你可以用 a 包裹起来以防止这种情况发生。

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

    【讨论】:

      【解决方案2】:

      启用动画和滑动

      并保留您现有的查询参数:有用,例如如果您的网址中有 :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 从路由对象中获取路径,如here 所述。

      【讨论】:

        【解决方案3】:
        //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">
            ....
        

        【讨论】:

          【解决方案4】:

          模板部分:

          <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

          【讨论】:

          • 我看到的问题是尝试执行此操作时,它最终会加载每个页面两次。如果您注意mounted(),您会看到它在使用选项卡进行路由时加载了两次页面。需要为此找到解决方法。 codesandbox.io/s/vuetify-v-tabs-with-vue-router-in1le watch console for "build loaded" 应该只执行一次,但实际上会挂载两次。
          • 我想通了。注意到您正在运行的路线视图循环。您不应该在选项卡数组中运行路由视图。导致它加载两次。
          • @timothymarois 你是绝对正确的。我更新了我的答案,现在它应该可以按预期工作了。
          • 如果组件需要父级的道具怎么办?
          • 为什么需要道具?如果props与router有关,可以通过router传递props。
          【解决方案5】:

          以下代码对我有用

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

          【讨论】:

          • 我第一次在选项卡下方得到拉伸线第一次如何解决这个问题。
          • 我过去曾按照您的方式进行操作,如果我没记错的话,它会在您的主组件安装时加载每个子组件。而使用:toto 道具仅在单击选项卡时才会加载相应的组件。我认为哪个更好。
          【解决方案6】:

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

          路由器

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

          【讨论】:

          • 谢谢!使用 :value="tab.route" 而不是 :to 和
          • @Stevie-RayHartog 是的,我更正了那个写错的路由器视图。为什么建议使用 :value 而不是 :to?
          • 我看到的问题是尝试执行此操作时,它最终会加载每个页面两次。如果您注意mounted(),您会看到它在使用选项卡进行路由时加载了两次页面。需要为此找到解决方法。 codesandbox.io/s/vuetify-v-tabs-with-vue-router-in1le watch console for "build loaded" 应该只执行一次,但实际上会挂载两次。
          • 我想通了。注意到您正在运行的路线视图循环。您不应该在选项卡数组中运行路由视图。导致它加载两次。
          • 要禁用标签滑动,只需使用touchless 属性,如&lt;v-tabs-items v-model="..." touchless&gt;
          【解决方案7】:

          我只是在此处添加一些与动画相关的提示并澄清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>
          

          请注意,您还可以在 v-tabv-tab-item 标记上使用 v-for 循环,只要在 v-tab-items 的 id 属性中找到您的 to 值。

          如果您需要将选项卡内容放置在与选项卡按钮不同的位置,这就是 v-tab-items 的用途。您可以将v-tab-items 放在v-tab-items 组件之外的v-tab-items 标记中。确保给它一个v-model="activeTab" 属性。

          【讨论】:

            【解决方案8】:

            更新

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

            原答案

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

            结果

            【讨论】:

            • 谢谢!如果有人感兴趣,我已经创建了您更新的小提琴的一个分支,以向选项卡添加默认路由:jsfiddle.net/thorne51/9g2zeow1/2
            • 如果你想将 props 传递给组件怎么办?
            • 问题是让动画在路线之间工作......
            猜你喜欢
            • 2019-11-24
            • 2019-08-20
            • 1970-01-01
            • 2019-06-10
            • 1970-01-01
            • 1970-01-01
            • 2020-09-29
            • 2018-03-24
            • 2017-07-14
            相关资源
            最近更新 更多