Добавление layout системы в существующий проект на Vue с использованием Vue router.
В данном примере будем рассматривать проект на Vue2 options api с использование Vue Router v3. В теории не вижу особых проблем в применении для Vue3 с composition api.
Задача: ускорить создание типовых страниц по средствам layout системы с возможностью передачи названия необходимого шаблона для страницы примерно похожей на реализацию в nuxt3.
Создаем директорию для наших шаблонов в корне проекта @/layouts
Оформляем основной компонент в котором будем определять нужный шаблон:
// @/layouts/index.vue <template> <component :is="layoutComponent"> </template> <script> import "Наш компонент с лейаутом" export default { name: "layout", components: { Наши компоненты layout }, computed: { // генерируем имя нужного нам компонента заранее импортированного // не делаю динамического импорта тк компонентов с лейаутами // как правило не много, при необходимости можно добавить layoutComponent() { return `layout-${this.layoutType}`; }, routeHaveLayoutType() { return this.$route.meta && 'layout' in this.$route.meta; }, // Определяем каой layout нам нужно использовать // для указания используем $router.meta, по умолчанию default // что будет равно нашему компоненту <layout-default /> layoutType() { if (this.routeHaveLayoutType) { return this.$route.meta.layout; } return "default"; }, } } </script>
Создаем рядом директорию для будущих шаблонов @/layouts/components
Создаем default шаблон для компонентов:
// @/layouts/components/default.vue <template> // Ваша логика для default layout // ключающая в себя router-view // Например <div class="container"> <header>My header</header> <main> <router-view /> </main> <footer>My footer</footer> </div> </template>
Создаем дополнительно ещё один произвольный шаблон tabs, который будет реализовывать страницу в виде табов, где табы — ссылка на страницу:
// @/layouts/components/tabs <template> <div class="container"> <div class="tabs"> <RouterLink v-for="(tab, index) in tabs" :key="index" :to="tab.path" class="tabs__item" active-class="is-active" > {{ tab.title }} </RouterLink> </div> <div class="tabs__content"> <router-view /> </div> </div> </template> <script> export default { name: "LayoutTabs", props: { tabs: { type: Array, default: () => [], }, }, } </script>
Создаем еще один дополнительный шаблон extra для демонстрации:
// @/layouts/components/extra <template> <div class="container"> <heading>Extra layout</heading> <router-view></router-view> </div> </template>
В корне проекта, в моем случае это App.vue в корне проекта, используем ранее созданный компонент <layout /> вместо существующего <router-view/>
<template> <div id="app"> // Было // <router-view /> // Стало <layout /> </div> </template>
Создаем базовую структуру router, согласно документации vue-router, в приложении либо используем существующую:
const routes = [ { path: "/", component: () => import(Ваш компонент страницы), ... } ... ]
Для определения нужного layout используем router.meta:
{ path: "/", components: () => import(Ваш компонент), meta: { layout: "extra", }, }
Для определения табов так же можно используем router.meta в родительском route, для этого нужно будет немного обновить код в нашем @/layouts/index:
<template> <component :is="layoutComponent" :tabs="layoutTabs" :parentRoute="parentRoute" /> </template> <script> import LayoutDefault from "./components/default.vue"; import LayoutExtra from "./components/extra.vue"; import LayoutTabs from "./components/tabs.vue"; export default { name: "Layout", components: { LayoutDefault, LayoutExtra, LayoutTabs, }, computed: { routeHaveLayoutType() { return this.$route.meta && 'layout' in this.$route.meta; }, parentRouteHaveLayoutType() { return this.parentRoute && this.parentRoute.meta && 'layout' in this.parentRoute.meta; }, routeHaveLayoutTabs() { return this.$route.meta && 'layoutTabs' in this.$route.meta; }, parentRouteHaveLayoutTabs() { return this.parentRoute && this.parentRoute.meta && 'layoutTabs' in this.parentRoute.meta; }, layoutType() { // проверяем содержит ли текущий route layout if (this.routeHaveLayoutType) { return this.$route.meta.layout; } // проверяем содержит ли родительский route layout if (this.parentRouteHaveLayoutType) { return this.parentRoute.meta.layout; } // возвращаем дефолтный по умолчанию если ничего не нашли return "default"; }, layoutComponent() { return `layout-${this.layoutType}`; }, layoutTabs() { if (this.layoutType === 'tabs') { // если в текущем route указаны табы для layout то используем их if (this.routeHaveLayoutTabs) { return this.$route.meta.layoutTabs; } // пробуем найти перечисление табов в родительском route if (this.parentRouteHaveLayoutTabs) { return this.parentRoute.meta.layoutTabs; } return []; } return []; }, // находим родительский route parentRoute() { const currentInMatched = this.$route.matched.find(i => i.regex.test(this.$route.path)); return currentInMatched && currentInMatched.parent; } }, } </script>
Если проект полностью на Vue и Vue-router, можно привязаться к children и из них формировать структуру дочерних табов, для этого обновим computed в компоненте <layout/>:
{ computed: { ... layoutTabs() { // при использовании parentRoute.children как табы if (this.parentChildren) { return this.parentChildren.map((i, k) => { return { path: i.path, title: 'tabTitle' in i.meta && i.meta.tabTitle || `tab${k}`, } }); } // при передачи табов как meta.layoutTabs if (this.layoutType === 'tabs') { if (this.routeHaveLayoutTabs) { return this.$route.meta.layoutTabs; } if (this.parentRouteHaveLayoutTabs) { return this.parentRoute.meta.layoutTabs; } return []; } return []; }, // находим children текущего route parentChildren() { if (!this.parentRoute) return []; const parentRoute = this.$router.options.routes.find(i => this.parentRoute.regex.test(i.path) ); return parentRoute && parentRoute.children; }, } }
При использовании в дочерних route своего собственного layout и наличии его собственных дочерних route оформляем код в формате:
[ { path: "default", component: () => import("компонент страницы"), }, { path: "tabs", redirect: "/tabs/red", meta: { layout: "tabs", layoutTabs: массив табов // если берем табы из meta }, children: [ { path: "red", component: () => import("компонент заглушка который состоит из одного <router-view />"), // кейс с еще более глубокой вложенностью и самостоятельным компонентом для родительского route "red" children: [ { // для корневого роута "red" path: "", component: () => import("ваш компонент red страницы"), }, { path: "orange", component: () => import("ваш компонент oragne страницы"), meta: { layout: "extra", }, }, { path: "tomato", component: () => import("ваш компонент tomato страницы"), meta: { layout: "extra", }, } ], }, { path: "green" component: () => import("ваш компонент green страницы"), }, { path: "blue", component: () => import("ваш компонент blue страницы"), } ] } ]
Полную реализацию примера можно посмотреть на Github
ссылка на оригинал статьи https://habr.com/ru/articles/829650/
Добавить комментарий