Motivação (SPA feita em Vue 3)
Quando trabalhamos em SPA's ( e em aplicações Web no geral), podemos nos deparar em páginas (views) que tem diferentes estruturas (layouts). Em certos casos, as diferenças são tão grandes que chega ser inviável (isso quando possível) contornar a situação via medias querys, por exemplo.
OBS: estou considerando que já tenha algum conhecimento prévio em Vue:
Veja só:
Imagine que sua aplicação tenhas páginas com layouts tão diferentes, como possibilitar isso de maneira legível? Tenha em mente que o próprio conteúdo do header e alt header, por exemplo, podem ser totalmente diferentes entre os layouts. Além da possibilidade de diferença semântica.
Vou propor uma alternativa, neste post, para SPA feita em Vue.
Case Nutris - link do repo
Nutris é uma PWA para cálculo de dietas parenterais. Sua primeira versão está no ar desde 2019 e agora está sendo refatorada, deixando código aberto no repo da open-ish.
Nossa home deslogada terá header e footer com conteúdos totalmente diferentes e não terá um menu de navegação. Já quando logado, além da mudança dos conteúdos, teremos um menu de navegação. Por fins didáticos, usarei exemplos simples de componentes para diferenciarmos os layouts e views.
Estrututa do projeto
Vamos criar dois layouts: default e alt (alternativo). Cada layout terá suas rotas e views respectivas (ou seja, a views dentro de default, são do layout default), referencias pelo próprio nome.
Então vamos lá, na raiz do projeto, teremos uma nova pasta de nome layouts.
Antes de vermos a rotas, vamos entender como ficarão nossos componentes. Lembrando que esse sistema de rotas usa vue-router
Mãos na massa
Vamos fazer nosso App.vue ser flexível usando a tag router-view sozinha, ao invés de inserirmos tags padrões (ex: header e footer) como comumente fazemos aqui.
App.vue
<template>
<router-view /> <!-- Components will render by router -->
</template>
Layout - Default 👨🏾🎨
src/layouts/defautt/Default.vue
<template>
<header>
Default Header
</header>
<main class="default">
<!-- Views'll render here -->
<router-view />
</main>
<footer>Default footer</footer>
</template>
Veja que dentro do layout, teremos um router-view que ficará responsável por renderizar os componentes de acordo com a pagina.
Views - Default
Vamos criar as views que serão renderizadas dentro dos layouts. Lembrando que as que estão na pasta default, fazem parte do layout default; e as que estão na pasta alt, fazem parte do layout alt.
src/views/default/Home.vue
<template>
<h2>Home page</h2>
</template>
src/views/default/About.vue
<template>
<h2>About page</h2>
</template>
Show!! Criado nossos componentes de exemplo para o layout default, vamos criar nossas rotas. Por fins didáticos, vou manter tudo no mesmo arquivo, mas em casos reais uso a estrutura que comentei a cima.
Rotas - Default
src/router/index.js 👀
// import components (layouts and views)
import DefaultLayout from '@/layouts/default/Default.vue'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
// Lets create default route, to default layout
const defaultRoute = {
path: '/',
component: DefaultLayout,
children: [
path: 'home',
component: Home, // it'll render at default layout
path: 'about',
component: About // it'll render at default layout
]
}
const routes = [defaultRoute]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
})
export default router
Rodando o projeto, na rota '/' teremos (PS: coloquei alguns estilos pra ficar mais bonitinho 😅🎨):
e na '/about':
Beleza, com isso, todas as nossas rotas terão a mesma estrutura (layout). Então vamos possibilitar que tenhamos um layout alternativo.
Layout - Alt 👨🎨
src/layouts/alt/Alt.vue
<template>
<header>
Alt Header
</header>
<section class="alt">
<!-- Views'll render here -->
<router-view />
</section>
<footer>Alt footer</footer>
</template>
Veja que no layout alt, temos a tag section ao invés da main. 😁😁
Views - Alt
src/views/alt/Alt.vue
<template>
<h2>Alt page</h2>
</template>
src/views/alt/SomeAlt.vue
<template>
<h2>SomeAlt page</h2>
</template>
Rotas
src/router/index.js 👀
// import components (layouts and views)
import DefaultLayout from '@/layouts/default/Default.vue'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
// Alt layout and views
import AltLayout from '@/layouts/alt/Alt.vue'
import Alt from '@/views/alt/Alt.vue'
import SomeAlt from '@/views/alt/SomeAlt.vue'
// Lets create default route, to default layout
const defaultRoute = {
path: '/',
component: DefaultLayout,
children: [
path: 'home',
component: Home, // it'll render at default layout
path: 'about',
component: About // it'll render at default layout
]
}
// Lets create alt route, to alt layout
const altRoute = {
path: '/alt',
component: AltLayout,
children: [
path: '',
component: Alt, // it'll render at alt layout
path: 'some-alt',
component: SomeAlt // it'll render at alt layout
]
}
const routes = [defaultRoute, altRoute]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
})
export default router
Veja como ficou a '/alt':
E a '/some-alt':
Voilà!!