Criando multi layouts em SPA`s Vue (2.x ou 3.x). 👨‍🎨👨🏾‍🎨
Tags: vue, spa, javascript, frontend
Published: 2020-10-03 14:50:44

## Motivação ([SPA](https://www.devmedia.com.br/ja-ouviu-falar-em-single-page-applications/39009) 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](https://vuejs.org/): 

Veja só: 

![Comparação entre layouts](https://dev-to-uploads.s3.amazonaws.com/i/0zvk0g59km7gwofe3x20.png)

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](https://github.com/open-ish/nutris)

Nutris é uma PWA para cálculo de dietas parenterais. Sua [primeira versão](https://nutris.firebaseapp.com/) está no ar desde 2019 e agora está sendo refatorada, deixando código aberto no repo da [open-ish](https://github.com/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](https://dev-to-uploads.s3.amazonaws.com/i/dw80qiasrce231jhrdpi.png)

Antes de vermos a rotas, vamos entender como ficarão nossos componentes. Lembrando que esse sistema de rotas usa [vue-router](https://next.router.vuejs.org/)

## 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
```html
<template>
  <router-view />  <!-- Components will render by router -->
</template>
```

###Layout - Default 👨🏾‍🎨

####src/layouts/defautt/Default.vue
```html
<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
```html
<template>
  <h2>Home page</h2>
</template>
```

####src/views/default/About.vue
```html
<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 👀

```javascript
// 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 😅🎨):

![rota home](https://dev-to-uploads.s3.amazonaws.com/i/me32kqrdet28nyfpem3i.png)

e na **'/about'**:

![rota about](https://dev-to-uploads.s3.amazonaws.com/i/ww1r6mkzq1lwl68l6pn7.png)

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
```html
<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
```html
<template>
  <h2>Alt page</h2>
</template>
```

####src/views/alt/SomeAlt.vue
```html
<template>
  <h2>SomeAlt page</h2>
</template>
```

### Rotas 

####src/router/index.js 👀

```javascript
// 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'**:

![rota alt](https://dev-to-uploads.s3.amazonaws.com/i/jznbestgkwozrk7wcmy4.png)

E a **'/some-alt'**:

![rota some-alt](https://dev-to-uploads.s3.amazonaws.com/i/1taycxv7mf9iq5v1y711.png)

`Voilà!!`

[See my other articles](https://frontend-pattern.vercel.app/articles)