vuejs с различными макетами (например, макет входа, макет страницы, регистрация и т.д.)

Я создал проект с помощью vue-cli. Я вижу, что в проекте есть один App.vue, который является основным макетом приложения - если я не ошибаюсь. Здесь я поместил свой основной макет HTML и <router-view></router-view>. Теперь проблема в том, что мне нужен совершенно другой макет для входа (разные обертки, у тела разные классы), но я не могу его изменить, так как у App.vue есть шаблон, который "фиксирован" как макет. Как подойти к этой проблеме? Рекомендуемый способ?

Должен ли я создавать новый компонент, который представляет макет, поэтому в этом случае мой шаблон App.vue будет иметь только <router-view></router-view> а затем LoginLayout.vue будет включен в него?

Ответ 1

Думаю, я нашел решение. Подход имеет App.vue содержащий только <router-view></router-view> а затем включает в себя различные компоненты, которые представляют макет (при необходимости, содержащий <router-view> и подпрограммы). Здесь я нашел проект, использующий его здесь.

Я думаю, что это держит вещи более чистыми и организованными. IMHO, скрывая все элементы, которые определяют структуру макета (все div), будет слишком грязным - особенно для больших приложений.

Ответ 2

Хорошим решением для этого является использование слотов

Сначала создайте свой "компонент компоновки",

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

Вы также можете иметь несколько слотов с именами, см. Полный документ.

Ответ 3

Я направляю свои приложения через макет. Например, для входа в систему не требуется структура, только компонент входа, но для других страниц требуется нижний колонтитул и т.д., Поэтому вот пример того, как я это делаю на своих маршрутах:

// 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')
    }
  }
}

Оба этих шаблона компоновки имеют тег маршрутизатора, поэтому вы можете создавать свои макеты по мере необходимости для разных частей приложения.

Ответ 4

Я нахожу другое решение, используя мета-маршрутизатор. Мне просто нужно, чтобы несколько компонентов нуждались в другом макете.

Я добавил мета-ключ plainLayout в src/router/index.js.

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

Затем визуализируйте макет условно с помощью playLayout в src/App.vue.

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

Смотрите демонстрационный проект здесь.

Ответ 5

Я динамически проверяю маршрут по всему миру на 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

Я знаю, что это старый, но nuxt.js макет поддержки в наши дни. Взгляни.

Ответ 7

Использование маршрутов, и, в частности, детские маршруты - отличный способ приблизиться к общим макетам в Vue.

Весь этот код использует Vue 2.x

Начните с наличия действительно простого компонента vue, называемого App, который не имеет макета.

app.vue

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

Затем введите файл маршрутов, который вы введете в свой экземпляр Vue.

Маршруты (ц | 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 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 (Login). Я решил сделать это так, потому что, если кто-то просто просматривает /account, я хочу приветствовать их с помощью экрана входа в систему. Может быть уместно для вашего приложения, что /account будет целевой страницей, где они смогут проверять историю заказов, изменять пароли и т.д.

Я сделал то же самое для каталога... / и /catalog загружая catalog/layout с файлом /catalog/catalog.

Также обратите внимание, что если вам не нравится идея наличия "подпапок" (например, учетная запись/логин вместо имени/входа), вы можете иметь псевдонимы, как я показываю в логине.

Добавив , alias: '/login' это означает, что пользователи могут просматривать /login даже если фактический маршрут - /account/login.

Это ключ ко всему этому, но просто попытаться сделать пример полным...

Вот мой загрузочный файл, который подключает мои app.vue и маршруты:

загрузки (ц | 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>

Оба макета используют общие компоненты, такие как верхний и нижний колонтитулы, но им это не нужно. В макете каталога есть категории в боковой навигационной панели, а в макете учетных записей нет. Я поместил свои общие компоненты под компоненты/общие.

общий /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 и конкретных файлов макета вместе с общими компонентами должна привести вас туда, где вы хотите быть.

Ответ 8

Я не знаю ни о каком "рекомендованном способе", но мое приложение структурировано так:

App.vue - просто верхняя строка меню (которая не отображается, когда пользователь не аутентифицирован) и <router-view></router-view> для каждого компонента (страницы)

Таким образом, каждая страница может иметь совершенно разные макеты.