64

我使用 vue-cli 生成了一个项目。我看到项目有一个 App.vue 这是应用程序的主要布局 - 如果我没记错的话。在这里,我放置了我的基本 HTML 布局和<router-view></router-view>. 现在的问题是我需要完全不同的登录布局(不同的包装器,body 有不同的类),但我无法更改它,因为 App.vue 的模板有点“固定”作为布局。如何处理这个问题?有推荐的方法吗?

我是否应该创建代表布局的新组件,这样在这种情况下,我的 App.vue 模板只有<router-view></router-view>然后 LoginLayout.vue 会包含在其中?

4

8 回答 8

50

我想我找到了解决办法。该方法App.vue仅包含<router-view></router-view>然后包含表示布局的不同组件(如果需要,包含<router-view>和子路由)。我在这里找到了一个以这种方式使用它的项目。

我认为它使事情更加干净和有条理。恕我直言,隐藏所有定义布局结构的元素(所有 div)太混乱了——尤其是对于更大的应用程序。

于 2016-06-16T09:09:57.167 回答
42

一个很好的解决方案是使用插槽

首先创建你的“布局组件”

src/components/layouts/basic.vue

<template>
  <div class="basic-layout">
    <header>[Company logo]</header>
    <hr>

    <slot/>

    <hr>
    <footer>
      Made with ❤ at Acme
    </footer>
  </div>
</template>

然后在另一个组件中使用它:

<template>
  <layout-basic>
    <p>Hello world!</p>
  </layout-basic>
</template>

<script>
  import LayoutBasic from '@/components/layouts/basic'
  export default {
    components: {
      LayoutBasic
    }
  }
</script>

“Hello world”将出现在<slot/>标签所在的位置。

您还可以有多个带有名称的插槽,请参阅完整的文档

于 2017-12-07T15:28:44.363 回答
11

我通过使用router meta找到了另一种解决方案。我只有几个组件需要另一种布局。

我在src/router/index.js添加了一个plainLayout元键。

export default new Router({
  mode: 'history',
  linkExactActiveClass: 'app-head-menu--active',
  routes: [
    {
      path: '/',
      component: Features,
    },
    {
      path: '/comics/:id',
      component: Comic,
      props: true,
    },
    {
      path: '/comics/:comic_id/:chapter_index',
      component: Chapter,
      props: true,
      meta: {
        plainLayout: true,
      },
    },
  ],
});

然后使用src/App.vue的playLayout 有条件地渲染布局。

<template>
  <div>
    <div v-if="!$route.meta.plainLayout">
      <div class="app-head">
      </div>
      <div class="app-content">
        <router-view/>
      </div>
    </div>

    <div v-if="$route.meta.plainLayout">
      <router-view/>
    </div>
  </div>
</template>

<script>
export default {
  name: 'app',
};
</script>

在此处查看演示项目。

于 2017-11-24T02:31:36.240 回答
9

利用路由,尤其是子路由是在 Vue 中实现通用布局的好方法。

所有这些代码都使用 Vue 2.x

首先有一个非常简单的名为 App 的 vue 组件,它没有布局。

应用程序.vue

<template>
    <router-view></router-view>
</template>

然后有一个 Routes 文件,你将把它带入你的 Vue 实例。

路线。(ts|js)

import Vue from 'vue'
import VueRouter from 'vue-router'

const NotFoundComponent = () => import('./components/global/notfound.vue')
const Login = () => import('./components/account/login.vue')
const Catalog = () => import('./components/catalog/catalog.vue')

export default new VueRouter({
    mode: 'history',
    linkActiveClass: 'is-active',
    routes: [
    //Account
    { path: '/account', component: () => import('./components/account/layout.vue'),
        children: [
            { path: '', component: Login },
            { path: 'login', component: Login, alias: '/login' },
            { path: 'logout', 
                beforeEnter (to: any, from: any, next: any) {
                    //do logout logic
                    next('/');
                } 
            },
            { path: 'register', component: () => import('./components/account/register.vue') }
        ]
    },

    //Catalog (last because want NotFound to use catalog's layout)
    { path: '/', component: () => import('./components/catalog/layout.vue'),
        children: [
            { path: '', component: Catalog },
            { path: 'catalog', component: Catalog },
            { path: 'category/:id', component: () => import('./components/catalog/category.vue') },
            { path: 'product', component: () => import('./components/catalog/product.vue') },
            { path: 'search', component: () => import(`./components/catalog/search.vue`)} ,
            { path: 'basket', component: () => import(`./components/catalog/basket.vue`)} ,
            { path: '*', component: NotFoundComponent }    
        ]    
    }        
    ]
})

该代码使用延迟加载(使用 webpack),所以不要让() => import(...)你扔。import(...)如果您想要急切加载,它可能只是。

重要的是儿童路线。所以我们设置了主要路径/account来使用,/components/account/layout.vue但是前两个孩子指定了主要内容 vue(登录)。我选择这样做是因为如果有人只是浏览到 /account 我想用登录屏幕问候他们。/account 可能适合您的应用程序作为登录页面,他们可以在其中检查订单历史记录、更改密码等...

我对目录做了同样的事情....../并且/catalog都加载catalog/layout/catalog/catalog文件。

另请注意,如果您不喜欢拥有“子文件夹”(即帐户/登录名而不仅仅是/登录名)的想法,那么您可以使用我在登录名中显示的别名。

通过添加, alias: '/login'它意味着用户可以浏览到/login即使实际路线是/account/login.

这是整个事情的关键,但只是为了尝试使示例完整......

这是我的启动文件,它连接了我的 app.vue 和路由:

启动。(ts|js)

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

import App from './components/app.vue';

import router from './routes';

new Vue({
    el: '#app',
    router,
    render: h => h(App)
});

我为我的应用程序的每个主要部分(帐户、目录等)创建了一个 layout.vue 文件。

帐户/layout.vue

<template>
<div>
    <cc-header></cc-header>

    <div class="container">
        <main>
            <router-view></router-view>
        </main>
        <aside>
        </aside>
    </div>

    <cc-footer></cc-footer>    
</div>
</template>

<script lang="ts">

import ccHeader from "../common/cc-header.vue"
import ccFooter from "../common/cc-footer.vue"

export default {
    components: {
        ccHeader,
        ccFooter
    }
}

</script>

<style lang="scss" scoped>

.container {
    display: flex;
}

main {
    flex: 3;
    order: 2;
}

aside {
    flex: 1;
    order: 1;
}
</style>

和目录的布局......

目录/layout.vue

<template>
<div>
<cc-header></cc-header>

<div class="catalog-container">
    <main class="catalog">
        <router-view></router-view>
    </main>
    <cc-categories></cc-categories>
</div>

<cc-footer></cc-footer>    
</div>

</template>

<script lang="ts">
import ccHeader from "../common/cc-header.vue"
import ccFooter from "../common/cc-footer.vue"

import ccCategories from "./cc-categories.vue"

export default {
    components: {
        ccCategories,
        ccHeader,
        ccFooter
    },
    data : function() : any {
    return {
        search: ''
    }        
},
}
</script>

<style lang="scss" scoped>
.catalog-container {
        display: flex;
    }

    .category-nav {
        flex: 1;
        order: 1;
    }

    .catalog {
        flex: 3;
        order: 2;
    }
</style>

两种布局都使用常见的组件,如页眉和页脚,但它们不需要。目录布局在侧导航中有类别,而帐户布局没有。我把我的常用组件放在 components/common 下。

常见/页脚.vue

<template>
<div>
    <hr />
    <footer>
        <div class="footer-copyright">
            <div>© Copyright {{year}} GlobalCove Technologies, LLC</div>
            <div>All rights reserved. Powered by CoveCommerce.</div>
        </div>
    </footer>
</div>
</template>

<script lang="ts">
    import Vue from "vue";
    export default Vue.component('cc-footer', {

        data : function() : any {
        return {
            year: new Date().getFullYear()
        }        
    },
    })

</script>

<style lang="scss">
</style>

整体文件结构

src/
    boot.ts
    routes.ts

    components/
        app.vue

        catalog/
            layout.vue
            catalog.vue
            category.vue
            product.vue
            search.vue
            basket.vue

        account/
            layout.vue
            login.vue
            register.vue

        global/
            notfound.vue

        common/
            cc-header.vue
            cc-footer.vue               

路由、简单的 app.vue 和特定布局文件的组合以及通用组件应该可以让您到达您想去的地方。

于 2018-01-23T15:47:09.157 回答
7

我通过布局路由我的应用程序。例如登录不需要结构,只需要登录组件,但其他页面需要页眉页脚等,所以这是我如何在路由中执行此操作的示例:

// application routes
'/secure': {
  name: 'secure',
  component: require('../components/layouts/default'),
  subRoutes: {
    '/home': {
      name: 'home',
      component: require('../components/home/index')
    }
  }
}

//- public routes
'/insecure': {
  name: 'insecure',
  component: require('../components/layouts/full-bleed'),
  subRoutes: {
    '/login': {
      name: 'login',
      component: require('../components/session/login')
    }
  }
}

这两个布局模板都有一个 router-view 标签,因此您可以根据应用程序的不同部分的需要构建布局。

于 2016-06-17T15:47:33.410 回答
3

我在 App.vue 上动态检查全局路由并使用它来确定需要显示的内容。

应用程序.vue

    <template>
      <div id="app">
        <top :show="show" v-if="show.header"></top>
        <main>
          <router-view></router-view>
        </main>
        <bottom v-if="show.footer"></bottom>
      </div>
    </template>

    <script>
    export default {
       mounted: function() {
         if(window.location.hash == "#/" || window.location.hash.indexOf('route')) {
            vm.show.header = true
            vm.show.footer = true
            vm.show.slideNav = true
          }
       }


       watch: {
         $route: function() {
           // Control the Nav when the route changes
           if(window.location.hash == "#/" || window.location.hash.indexOf('route')) {
             vm.show.header = true
             vm.show.footer = true
             vm.show.slideNav = true
           }
         }
       }
    }
    </script>

这样我也可以通过道具控制顶部和底部导航中显示的内容。

希望这可以帮助!

于 2017-04-11T11:52:23.363 回答
0

我不知道任何“推荐方式”,但我的应用程序结构如下:

App.vue- 只是顶部菜单栏(当用户未通过身份验证时不呈现)和<router-view></router-view>每个组件(页面)

所以每个页面都可以有完全不同的布局。

于 2016-06-15T16:21:22.037 回答
0

评论接受的答案

有点不同意这一点。有同样的问题,这个答案让我很困惑。基本上,当您有一个想要在应用程序中随处重复使用的组件(例如页脚、页眉)时,您可以将其保存在App.vue. 这是我的情况,我想在每一页都有页脚和页眉,发现这个答案让我走错了方向,但你可以做到,它确实有效,例如App.vue

<template>
  <div id="app">
    <app-header />
    <router-view />
    <app-footer />
  </div>
</template>

<script lang="ts">
// Imports related to Vue.js core.
import { Component, Vue } from "vue-property-decorator";

// Imports related with custom logic.
import FooterComponent from "@/components/Footer.vue";
import HeaderComponent from "@/components/Header.vue";

@Component({
  components: {
    "app-footer": FooterComponent,
    "app-header": HeaderComponent
  }
})
export default class App extends Vue {}
</script>

<style lang="scss" scoped>
</style>

Footer.vue(位于components/Footer.vue):

<template>
  <div>
    <footer>
      <div>&copy; {{ year }} MyCompany</div>
    </footer>
  </div>
</template>

<script lang="ts">
// Imports related to Vue.js core.
import { Component, Vue } from "vue-property-decorator";

@Component({})
export default class FooterComponent extends Vue {
  public year = new Date().getFullYear();
}
</script>

<style lang="scss" scoped>
</style>

Header.vue(位于components/Header.vue):

<template>
  <div>
    <header>
      <router-link to="/">Home</router-link>
      <router-link to="/about">About</router-link>
      <router-link to="/contact">Contact</router-link>
    </header>
  </div>
</template>

<script lang="ts">
// Imports related to Vue.js core.
import { Component, Vue } from "vue-property-decorator";

@Component({})
export default class HeaderComponent extends Vue {}
</script>

<style lang="scss" scoped>
</style>
于 2019-11-14T14:48:00.723 回答