【问题标题】:How to hide global component (e.g navbar) on some routes?如何在某些路线上隐藏全局组件(例如导航栏)?
【发布时间】:2021-12-15 13:17:38
【问题描述】:

因此,我的 vuejs SPA 上的绝大多数视图顶部都有一个导航栏组件。但是,某些选择视图(例如登录)不应显示此内容。

我的解决方法是导入导航栏并在需要时单独添加到每个视图中,但现在我可以看到当从一个视图转到另一个视图时它会导致奇怪的闪烁 - 大概是因为导航栏组件正在被删除,并且立即重新添加。这似乎是错误的。

我假设有一些方法可以在 App.vue 中添加导航栏组件,但有条件地使用 vue-router 在某些路由上隐藏它?这方面的最佳做法是什么?

【问题讨论】:

    标签: vue.js vuejs2 vue-router


    【解决方案1】:

    一种常见的方法是使用嵌套路由。这是我的一个项目中的一个示例:

    const router = new Router({
        mode: 'history',
        base: process.env.BASE_URL,
        routes: [
            {
                path: '/login',
                name: 'login',
                component: LoginView,
            },
            {
                path: '/admin',
                name: 'admin',
                component: AdminShell,
                children: [
                    {
                        path: 'home',
                        name: 'admin_home',
                        component: AdminHomeView
                    },
                ]
            },
        ],
    });
    

    主视图将只有一个路由器视图组件,因此登录视图组件可以在没有导航栏的情况下显示。

    <!-- App -->
    <v-app>
        <v-content>
            <router-view> </router-view>
        </v-content>
    </v-app>
    

    管理外壳将被注入到主视图中,它将包含导航栏、侧边栏以及它需要的任何其他内容。

    <!-- Admin shell -->
    <v-container fill-height>
        <v-toolbar color="blue darken-3" dark app :clipped-left="$vuetify.breakpoint.mdAndUp" fixed>
            <v-toolbar-title style="width: 300px" class="ml-0 pl-3">
                <v-toolbar-side-icon @click.stop="drawer = !drawer"></v-toolbar-side-icon>
                <span class="hidden-sm-and-down">Admin</span>
            </v-toolbar-title>
            <v-spacer></v-spacer>
            <UserInfo></UserInfo>
        </v-toolbar>
    
        <!-- Nested route -->
        <router-view></router-view>
    </v-container>
    

    然后,AdminHomeView 将被注入到 admin shell 视图的嵌套路由器视图中。

    【讨论】:

    • 完全按照我的做法!
    • 我不知道我应该把答案中的代码放在哪里。栈中的答案有admin shell和v-container,但是不知道应该把路由或者代码放在app view文件的哪里
    【解决方案2】:

    Route Meta Fields 为我解决了这个问题:

    //router.js
    {
     path: '/login',
     name: 'login',
     component: () => import(/* webpackChunkName: 'login' */ '@/components/Login.vue'),
     meta: {
      hideNavbar: true,
     }
    }
    
    //App.vue
    <Navbar v-if="!$route.meta.hideNavbar" />
    

    【讨论】:

      【解决方案3】:

      与@Edins 嵌套路由方法相比,我更喜欢主视图中的条件渲染组件。

      Vue 通过Vuex 提供了一个很棒的全局状态存储,这也让这变得非常简单。

      典型的例子是只有在用户登录时才有导航栏。
      因此,您在状态存储中跟踪登录状态,然后在您的主视图(或 App.vue)中使用计算的 getter:

      <template>
        <navbar v-if="isAuthenticated"/>
        <router-view/>
      </template>
      
      export default {
      name: 'App',
      computed: { 
        isAuthenticated() {
          return this.$store.getters.isAuthenticated
        },
      },
      

      【讨论】:

      • 我注意到这种方法的一个问题是每次视图更改时都会重新渲染导航栏,这会导致奇怪的闪烁效果,因为导航栏会消失一瞬间然后又回来。有没有办法解决这个问题?
      • 这种方法不会在路由更改时重新渲染导航栏(可通过在创建或挂载的导航栏中记录某些内容来验证)。导航栏和计算属性在&lt;router-view/&gt; 之外,因此路由更改不会导致重新渲染。如果你有,你应该分享你的代码,这样我们就可以尝试帮助确定问题@danmartin
      • 在我的例子中,我只是想在主页上隐藏brand(因为它已经显示在英雄部分),所以我作为道具传递给导航栏组件。
      【解决方案4】:

      我们可以通过使用 v-if 指令检查当前路由的名称来有条件地渲染组件。我在最近的一个项目中使用了它。

      我的路线 index.js 文件

      import Vue from 'vue'
      import VueRouter from 'vue-router'
      import Home from '../views/Home.vue'
      import Login from '../views/Login.vue'
      
      Vue.use(VueRouter)
      
      const routes = [
        {
          path: '/',
          name: 'home',
          component: Home
        },
        {
          path: '/login',
          name: 'login',
          component: Login
        },
        {
          path: '/register',
          name: 'register',
          component: Register
        }  
      ]
      
      const router = new VueRouter({
        mode: 'history',
        base: process.env.BASE_URL,
        routes
      })
      
      export default router
      

      我的 App.vue 文件

      <template>
          <div id="app">
      
      /* Note that this takes an array of route names, so you can simply pass in
      the name of the route you don't want this component to be displayed on */
      
              <navbar v-if="!['login', 'register', 'help'].includes($route.name)" />
          
              <main>
                  <router-view />
              </main>
          
              <appfooter v-if="!['login', 'register'].includes($route.name)"/>
          
          </div>
          </template>
      

      【讨论】:

      • 它复制了@TommyF 所说的
      • 为什么不使用在应用程序中编写代码,看看是否没有任何错误。如果您希望它为您必须编写不同逻辑的任何其他路线实现此功能,他的方法仅适用于登录屏幕。我的适用于您想要的任何路线,只需将其名称传递到数组中即可。我也很确定您可以看到他的方法不是检查路线名称,而是当问题说“在某些路线中”时,如果用户登录了
      • 好吧,这是有道理的,但在你的描述中,我看到你强调v-if 指令,就像它是重点一样。并且v-if 指令在先前的答案中被使用。所以是的,你是对的,你的方法不同,但请更改答案的描述以强调它。
      • 嗯,没错,但用“v-if”很难解释。
      • 好吧,从技术上讲,在你编辑你的帖子之前,我不能收回我的投票
      【解决方案5】:

      在路由器中-> index.js 编写子路由并在父路由中写入-此时写入的任何全局组件, 并在孩子们写下我们想展示的地方

      const router = new VueRouter({
        mode: "history",
        base: process.env.BASE_URL,
        routes: [
          { path: "/", redirect: "/login" },
          { path: "/registration", component: registration },
      
          {
            path: "/home",
            component: navbar,
            children: [
              {
                path: "/home",
                name: "home",
                component: () => import("../views/Home.vue"),
              },
              {
                path: "/notes",
                name: "notes",
                component: () => import("../views/notes/Index.vue"),
              },
              {
                path: "/notes/create",
                name: "notescreate",
                component: () => import("../views/notes/Create.vue"),
              },
              {
                path: "/notes/edit/:id",
                name: "notesedit",
                component: () => import("../views/notes/Edit.vue"),
              },
              { path: "/myposts", component: myPosts },
              { path: "/allposts", component: allPosts },
              { path: "/create", component: create },
              { path: "/edit/:id", component: edit },
            ],
          },
          {
            path: "/login",
            name: "login",
            component: () => import("../views/Login.vue"),
          },
        ],
      });
      

      别忘了写在app.vue中

      没有路由器视图 - 它不会工作

      【讨论】:

        【解决方案6】:

        我知道它很旧,但我使用了不同的方式。

        只需观察路线名称的变化。我喜欢使用auth.loginauth.register 作为我的路线。

        <v-app-bar v-if="!currentRouteName.startsWith('auth')"></v-app-bar>
        

        然后:

        data() {
            return{
                currentRouteName: 'home',
            }
        },
        watch:{
            ['$route.name'](route) {
                this.currentRouteName = route
            }
        }
        

        【讨论】:

        • 欢迎来到 Stack Overflow,感谢您的贡献。为旧问题提供新方法很好,但解释为什么你的方法是首选的方法很有用——或者,更有可能的是,在什么场景下它可能是首选的.您可以编辑您的答案以提供该解释吗?这是新语法吗?它更快吗?它是否与不同的组件更好地集成?
        【解决方案7】:

        对于不想使用v-ifv-show 的人,您可以使用命名视图。
        https://router.vuejs.org/guide/essentials/named-views.html

        【讨论】:

        • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
        猜你喜欢
        • 2019-04-05
        • 2017-08-24
        • 2021-01-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-03
        • 2016-03-31
        • 1970-01-01
        相关资源
        最近更新 更多