ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

对于任何好奇的人,这里有一个链接,其中包含使用 vue 管理布局的有趣选项:markus.oberlehner.net/blog/dynamic-vue-layout-components
一个好的解决方案是:levelup.gitconnected.com/…

N
Nathaniel Ford

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

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


该链接项目中显示的子路线很好地布置了事情!
我更喜欢这种方法,因为所有组件的路由都在来自 CoPilot 的路由器中。
Vue 路由器指南:Essentials - Nested Routes 中也描述了这种方法。
命名插槽是您要查找的内容:vuejs.org/v2/guide/components-slots.html#Named-Slots
这个答案实际上还不清楚。很惊讶它有这么多的赞成票。
t
tony19

一个很好的解决方案是使用 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


我认为这是最好的方法,因为这正是插槽的用途。
完美的。无需使用 3rd 方库/组件。
这很容易管理,但可能会导致一些问题:来自 hereAlthough, in terms of flexibility, this approach has everything we need, there is one huge downside of wrapping our views in a static layout component: the component is destroyed and re-created every time the route changes.
这是正确的答案。查看命名槽:vuejs.org/v2/guide/components-slots.html#Named-Slots
C
Chad Carter

利用路由,尤其是子路由是在 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/catalog 文件加载 catalog/layout

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

添加 , alias: '/login' 意味着即使实际路径是 /account/login,用户也可以浏览到 /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 和特定布局文件的组合以及通用组件应该可以让您到达您想去的地方。


l
lingceng

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


您的方法与@user2343398 的方法相结合,可以完美地完成工作。
T
Tremendus Apps

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

// 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 标签,因此您可以根据应用程序的不同部分的需要构建布局。


你能为此提供一个例子吗?
C
Community

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

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

希望这可以帮助!


这在小情况下可能很有用,但可能会变得非常混乱并且容易出错。
l
lukpep

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

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

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


这不是那么简单,我拥有的模板在登录结构上完全不同,或者说仪表板 - 不仅仅是显示隐藏几个元素。
它可能是。整个 html 内容可能不同
D
Daniel Danielecki

评论接受的答案

有点不同意这一点。有同样的问题,这个答案让我很困惑。基本上,当您有一个想要在应用程序中随处重复使用的组件(例如页脚、页眉)时,您可以将它保存在 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>

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅