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>
для каждого компонента (страницы)
Таким образом, каждая страница может иметь совершенно разные макеты.