具有不同布局的vuejs应用程序(例如登录布局,页面布局,注册等)

时间:2016-06-15 14:22:27

标签: vue.js vue-router

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

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

9 个答案:

答案 0 :(得分:37)

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

我认为它可以使事情变得更加整洁有序。恕我直言,隐藏所有定义布局结构的元素(所有div)都会太乱 - 特别是对于更大的应用程序。

答案 1 :(得分:30)

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

首先创建“布局组件”

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/>标记所在的位置。

您还可以拥有多个名称的插槽,请参阅complete docs

答案 2 :(得分: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')
    }
  }
}

这两个布局模板都有一个路由器视图标记,因此您可以根据需要为应用的不同部分构建布局。

答案 3 :(得分:6)

使用路由,特别是子路由是在Vue中使用公共布局的好方法。

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

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

<强> app.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(登录)。我选择这样做是因为如果有人只是浏览/帐户我想用登录屏幕问候他们。您的应用可能适合/帐户是一个登录页面,他们可以检查订单历史记录,更改密码等...

我为目录做了同样的事情...... //catalog都将catalog/layout加载到/catalog/catalog文件。

另请注意,如果您不喜欢拥有&#34;子文件夹&#34; (即帐户/登录而不仅仅是/ login)然后你可以在我登录时显示别名。

通过添加, 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文件。

帐户/ l​​ayout.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下。

<强>公共/ footer.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和特定的布局文件以及常用组件的组合应该可以让您到达目的地。

答案 4 :(得分:5)

我使用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>

查看演示项目here

答案 5 :(得分:1)

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

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

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

希望这有帮助!

答案 6 :(得分:0)

我不知道任何&#34;推荐方式&#34;但我的应用程序结构如下:

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

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

答案 7 :(得分:0)

我知道它现在已经过时了但nuxt.js支持布局。看看。

答案 8 :(得分: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>