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

Как мы видим, у различных страниц общая шапка. Сайт.
А что, если нам необходимо сделать несколько шаблонов для разных
страниц или состояний приложения? Этим мы сейчас и
займемся.
Первым делом нам необходимо Vue Js приложение с подключенным
роутером.
Что из себя представляют шаблоны? Правильно, обычные компоненты,
которые будут вызываться в зависимости от того или иного условия,
будь то активная странница, статус аутентификации или (по бреду)
время суток.
Подготовим основной компонент для работы с шаблонами.
Если вы создавали проект через Vue-Cli у вас он выглядит примерно
так:
//ВАШ_ПРОЕКТ/src/App.vue<template> <div id="app"> <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/about">About</router-link> </div> <router-view/> </div></template><style>#app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50;}#nav { padding: 30px;}#nav a { font-weight: bold; color: #2c3e50;}#nav a.router-link-exact-active { color: #42b983;}</style>
Нам необходимо добавить новое вычисляемое свойство (Computed) в
секцию script (если у вас ее нет, скопируйте ее из любого vue
компонента).
Это свойство будет возвращать нам имя компонента-шаблона в
зависимости от того или иного условия, в данном примере шаблон
будет привязан к странице.
//ВАШ_ПРОЕКТ/src/App.vue...<script> export default { computed: { //Это самое вычисляемое свойство layout(){ //Вернем имя шаблона из роута или дефолтное значение //(шаблон для страниц, для которых мы не указали шаблон) return this.$route.meta.layout || "default-layout" } } }</script>...
Отредактируем секцию template добавим в нее динамический
компонент, который будет меняться в зависимости от значения
layout.
//ВАШ_ПРОЕКТ/src/App.vue<template> <div id="app"> <!--Динамический компонент--> <component :is="layout"> <router-view/> </component> </div></template>...
Теперь создадим пару шаблонов.
Для удобства будем хранить их в отдельной папке layouts.

По значимости папка не сильно ушла от components или view, просто удобно.
Есть у Vue такой элемент slot, который позволит нам подставлять
наши представления в компонент-шаблон. При отрисовке он заменяется
на контент, который мы укажем при вызове компонента. Создадим три
шаблона, пусть они будут одинаковыми, с разницей в цвете шапки и
подвала, для наглядности.
Синий шаблон, дефолтный:
//ВАШ_ПРОЕКТ/src/layouts/Default.vue<template> <div> <header> <ul class="nav"> <li><router-link class="link" to="/">Домашняя страница</router-link></li> <li><router-link class="link" to="/page2">Страница 1</router-link></li> <li><router-link class="link" to="/page3">Страница 2</router-link></li> <li><router-link class="link" to="/page4">Страница 2</router-link></li> </ul> </header> <section class="content"> <!--Элемент, который при отрисовке будет заменен на ваш view--> <slot/> </section> <footer> </footer> </div></template><script> export default { name: "Default" }</script><style scoped> header{ background-color: blue; height: 70px; display: flex; align-items: center; } footer{ background-color: blue; height: 70px; } .content{ min-height: calc(100vh - 140px); } ul{ list-style: none; margin: 0; color: white; } li{ color: white; display: inline; margin: 0 5px; } .link{ color: white; text-decoration: none; }</style>
Зеленый шаблон:
//ВАШ_ПРОЕКТ/src/layouts/Green.vue<template> <div> <header> <ul class="nav"> <li><router-link class="link" to="/">Домашняя страница</router-link></li> <li><router-link class="link" to="/page2">Страница 1</router-link></li> <li><router-link class="link" to="/page3">Страница 2</router-link></li> <li><router-link class="link" to="/page4">Страница 2</router-link></li> </ul> </header> <section class="content"> <!--Элемент, который при отрисовке будет заменен на ваш view--> <slot/> </section> <footer> </footer> </div></template><script> export default { name: "green" }</script><style scoped> header{ background-color: green; height: 70px; display: flex; align-items: center; } footer{ background-color: green; height: 70px; } .content{ min-height: calc(100vh - 140px); } ul{ list-style: none; margin: 0; color: white; } li{ color: white; display: inline; margin: 0 5px; } .link{ color: white; text-decoration: none; }</style>
Красный шаблон:
//ВАШ_ПРОЕКТ/src/layouts/Red.vue<template> <div> <header> <ul class="nav"> <li><router-link class="link" to="/">Домашняя страница</router-link></li> <li><router-link class="link" to="/page2">Страница 1</router-link></li> <li><router-link class="link" to="/page3">Страница 2</router-link></li> <li><router-link class="link" to="/page4">Страница 2</router-link></li> </ul> </header> <section class="content"> <!--Элемент, который при отрисовке будет заменен на ваш view--> <slot/> </section> <footer> </footer> </div></template><script> export default { name: "Red" }</script><style scoped> header{ background-color: red; height: 70px; display: flex; align-items: center; } footer{ background-color: red; height: 70px; } .content{ min-height: calc(100vh - 140px); } ul{ list-style: none; margin: 0; color: white; } li{ color: white; display: inline; margin: 0 5px; } .link{ color: white; text-decoration: none; }</style>
Теперь зарегистрируем эти компоненты-шаблоны в нашем Vue.
//ВАШ_ПРОЕКТ/src/main.jsimport Vue from 'vue'import App from './App.vue'import router from './router'//Подключим файлы компонентовimport DefaultLayout from "./layouts/Default"import GreenLayout from "./layouts/Green"import RedLayout from "./layouts/Red"//И зарегистрируем их в нашем приложенииVue.component("default-layout", DefaultLayout)Vue.component("green-layout", GreenLayout)Vue.component("red-layout", RedLayout)Vue.config.productionTip = falsenew Vue({ router, render: h => h(App)}).$mount('#app')
Отлично! Шаблоны готовы, давайте создадим четыре
представления(страницы), два из которых будут отрисовываться с
синим шаблоном, а два остальных с красным и зеленым. Они тоже будут
одинаковыми, только с разными заголовками.
//ВАШ_ПРОЕКТ/src/views/page1.vue<template> <div> <h1>Синий шаблон</h1> </div></template><script> export default { name: "Page1" }</script><style scoped>
</style>//ВАШ_ПРОЕКТ/src/views/page2.vue<template> <div> <h1>Зеленый шаблон</h1> </div></template><script> export default { name: "Page2" }</script><style scoped></style>
//ВАШ_ПРОЕКТ/src/views/page3.vue<template> <div> <h1>Красный шаблон</h1> </div></template><script> export default { name: "Page3" }</script><style scoped></style>
//ВАШ_ПРОЕКТ/src/views/page4.vue<template> <div> <h1>Еще один синий шаблон</h1> </div></template><script> export default { name: "Page4" }</script><style scoped></style>
Теперь добавим роуты под наши страницы, и в мета-данных укажем
какой шаблон им использовать при отрисовке.
import Vue from 'vue'import VueRouter from 'vue-router'//Подключим наши страницыimport Page1 from "../views/Page1"import Page2 from "../views/Page2"import Page3 from "../views/Page3"import Page4 from "../views/Page4"Vue.use(VueRouter)const routes = [ { path: '/', name: 'Home', component: Page1 //Так, как синий шаблон у нас является дефолтным, его можно не указывать в мета-данных }, { path: '/page2', name: 'Page2', component: Page2, //А вот это свойство как раз будет содержать название компонента-шаблона, //который мы хотим использовать для данной страницы meta:{ layout: "green-layout" } }, { path: '/page3', name: 'Page3', component: Page3, meta:{ layout: "red-layout" } }, { path: '/page4', name: 'Page4', component: Page4, //И снова ничего не указываем, чтобы задействовать дефолтный шаблон }]const router = new VueRouter({ mode: 'history', base: process.env.BASE_URL, routes})export default router
Запускаем наше приложение и проверяем:

Целиком код можно посмотреть тут