Criando multi layouts em SPA`s Vue (2.x ou 3.x). 👨‍🎨👨🏾‍🎨

Tássio - Oct 3 '20 - - Dev Community

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

Comparação entre layouts

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.

Estrutura do projeto

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

src/views/default/About.vue

<template>
  <h2>About page</h2>
</template>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Rodando o projeto, na rota '/' teremos (PS: coloquei alguns estilos pra ficar mais bonitinho 😅🎨):

rota home

e na '/about':

rota 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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

src/views/alt/SomeAlt.vue

<template>
  <h2>SomeAlt page</h2>
</template>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Veja como ficou a '/alt':

rota alt

E a '/some-alt':

rota some-alt

Voilà!!

See my other articles

. . . . . . . . . .