menu

SHARKLABS

Vuepress: Como Criar Temas Personalizados

/
/
Vuepress: Como Criar Temas Personalizados
bookmark Vuepress, Vue.js access_time

Requisitos

Antes de iniciar este artigo quero deixar algumas recomendações e dicas de leitura para que você assimile bem o conteúdo.

Primeiramente, é importante que você tenha um bom entendimento sobre Vue.js. Não precisa ser um usuário avançado, mas precisa ter uma noção sobre o uso de componentes.

Você também precisa ter um claro entendimento de como o Vuepress funciona. Se você tem pouca experiência com Vuepress, recomendo que você leia estes dois artigos que escrevi recentemente:

Se você leu os artigos citados acima, você sabe que o Vuepress é uma ferramenta que tem como principal objetivo gerar sites estáticos e com foco em conteúdo. Neste caso, o conteúdo do site são os arquivos markdown e o objetivo do tema é criar uma estrutura HTML com estilos personalizados ao redor desse texto.

Tema para Documentação

Digamos que você pretende criar um site para documentação de uma ferramenta e que não tenha o formato de blog. Para este exemplo, vamos supor que seus arquivos do Vuepress estejam dentro da pasta "docs".

Inicialmente crie a seguinte estrutura de pastas "docs/.vuepress/theme/layout/". Dentro desta pasta, crie o arquivo "Layout.vue" e coloque o seguinte código:

<template>
  <div>
    <div>Layout Begin</div>
    <Content />
    <div>Layout End</div>
  </div>
</template>

<script>
export default {
  name: 'Layout',
};
</script>

A tag <Content /> é onde o conteúdo dos arquivos markdown será carregado e ao redor dela você pode fazer personalizações, inclusive chamando componentes Vue.js. Mais à frente falaremos sobre isso.

Desta forma, todos os arquivos serão carregados dentro dessa estrutura. Caso você queira fazer uma estrutura diferente para alguns arquivos markdown, basta você criar outro arquivo dentro da pasta "docs/.vuepress/theme/layout/" e referenciar no frontmatter.

Por exemplo, crie o arquivo "docs/.vuepress/theme/layout/AnotherLayout.vue":

<template>
  <div>
    <div>AnotherLayout - Begin</div>
    <Content />
    <div>AnotherLayout - End</div>
  </div>
</template>

<script>
export default {
  name: 'AnotherLayout',
};
</script>

A referência no frontmatter do arquivo markdown deve ser feito dessa forma:

---
layout: AnotherLayout
---

GlobalLayout

Caso você tenha vários layouts e queira compartilhar áreas comuns entre eles, você pode usar um GlobalLayout. Aqui estou falando da possibilidade de inserir um cabeçalho ou um rodapé padrão em todas as páginas, independente do layout escolhido.

Para isso basta criar o arquivo "docs/.vuepress/theme/layout/GlobalLayout.vue" com o codigo abaixo:

<template>
  <div>
    <div>GlobalLayout - Begin</div>
    <LayoutSelected />
    <div>GlobalLayout - End</div>
  </div>
</template>

<script>
import LayoutSelected from '@app/components/GlobalLayout.vue';

export default {
  name: 'GlobalLayout',
  components: { LayoutSelected },
};
</script>

Agora todos os layouts e consequentemente todas as páginas serão carregadas dentro desta estrutura.

NotFound / 404

Para gerar uma página de erro HTTP 404 (não encontrado), é só criar o arquivo "docs/.vuepress/theme/layout/NotFound.vue" e personalizar a sua aparência.

Só uma pequena observação, eu percebi nos meus testes que a página 404 só é gerada caso exista o arquivo "docs/404.md".

Componentes

Até então eu coloquei uma estrutura muito simples ao redor do conteúdo, mas provavelmente você terá uma estrutura mais complexa e importará outros componentes.

Por exemplo, crie o arquivo "docs/.vuepress/theme/components/Header.vue":

<template>
  <div>My Header</div>
</template>

<script>
export default {
  name: 'Header',
};
</script>

E agora importe esse componente no GlobalLayout:

<template>
  <div>
    <Header />
    <LayoutSelected />
    <div>GlobalLayoutEnd</div>
  </div>
</template>

<script>
import LayoutSelected from '@app/components/GlobalLayout.vue';

import Header from '@theme/components/Header.vue';

export default {
  name: 'GlobalLayout',
  components: { LayoutSelected, Header },
};
</script>

Preste atenção no caminho de importação: @theme/components/

É obrigatório que todos os componentes do seu tema sejam importados seguindo este caminho. Neste link da documentação oficial tem mais detalhes sobre a estrutura de diretório recomendada.

Metadata

O Vuepress disponibiliza alguns dados (chamados de metadata) sobre o site e a página acessada para que você personalize seus layouts e componentes. Estes dados podem ser ser acessados por meio do comando "this".

export default {
  name: 'GlobalLayout',
  mounted() {
    console.log(this);
    console.log(this.$site);
    console.log(this.$page);
    console.log(this.$frontmatter);
    console.log(this.$themeConfig);
  },
};

Vou explicar de uma maneira bem geral cada um desses metadata:

  • $site: Dados de todo o projeto, inclusive com todas as páginas.
  • $page: Dados somente da página atual.
  • $frontmatter: Dados do frontmatter da página atual.
  • $themeConfig: Configurações do tema especificados no arquivo "docs/.vuepress/config.js".

Para mais detalhes sobre os metadata, consulte a documentação oficial por meio deste link.

CSS

Sobre o arquivos CSS, você deve inseri-los na pasta "docs/.vuepress/theme/styles" e depois é só carregá-los dentro do "GlobalLayout.vue".

<template>
  <div>
    <LayoutSelected />
  </div>
</template>

<script>
import LayoutSelected from '@app/components/GlobalLayout.vue';

import '@theme/styles/index.css'; // IMPORTANT HERE

export default {
  name: 'GlobalLayout',
  components: { LayoutSelected },
};
</script>

Imagens

Na documentação do Vuepress eu não encontrei uma orientação para as imagens do tema, mas eu prefiro colocá-las dentro da pasta "docs/.vuepress/theme/assets/img".

Depois é só importar dentro de algum componente:

<template>
  <div>
    <img :src="anyImage" alt="Any Image for Example" />
  </div>
</template>

<script>
import anyImage from '@theme/assets/img/anyImage.svg';

export default {
  name: 'AnyComponent',
  data: () => ({
    anyImage,
  }),
};
</script>

Utilizando Plugins

Para você utilizar plugins do Vuepress dentro do seu tema, crie o arquivo "docs/.vuepress/theme/index.js" com o seguinte conteúdo:

// docs/.vuepress/theme/index.js
module.exports = (themeConfig, ctx) => {
  return {
    plugins: [['vuepress-plugin-example-fictitious']], // It's a example
  };
};

Vale ressaltar que os outros plugins declarados no arquivo "docs/.vuepress/config.js" não tem uma relação direta com o seu tema.

Plugin para Blog

Se você leu os artigos que eu recomendei no início desta publicação, você sabe que é necessário instalar o plugin @vuepress/plugin-blog para que o Vuepress funcione como um blog. Tudo expliquei até aqui continua valendo. O que muda é que esse plugin executa outros Layouts e adiciona mais alguns metadata.

Primeiramente, vamos instalar e configurar este plugin no nosso tema. Siga estes passos:

npm install @vuepress/plugin-blog --save
// docs/.vuepress/theme/index.js
module.exports = (themeConfig, ctx) => {
  return {
    plugins: [
      [
        '@vuepress/plugin-blog',
        {
          directories: [{ id: 'post', dirname: '_posts', path: '/' }],
          frontmatters: [{ id: 'tag', keys: ['tags'], path: '/tag/' }],
        },
      ],
    ],
  };
};

Blog Vuepress: Layouts

Quando você acessar a página principal do blog ("/"), em vez de carregar o "Layout.vue" o plugin tenta encontrar o arquivo "IndexPost.vue" (que deve ficar dentro da pasta Layout). Ou seja, você pode criar um layout específico para sua página inicial (com links, banners e o que mais você desejar).

Dentro deste layout é disponibilizado o metadata this.$pagination que contém os últimos post e seguindo as regras de páginação do plugin. Para saber mais sobre as regras e configurações de paginação clique aqui.

A partir da segunda página de posts/artigos, outro layout é carregado: "DirectoryPagination.vue". Desta forma você tem a possibilidade de mostrar uma estrutura diferente quando o usuário começa a procurar posts mais antigos.

Quando você acessar um post/artigo, o plugin carregará o layout "Post.vue" e aqui você pode utilizar a tag <Content />, uma vez que neste caso há um arquivo markdown relacionado.

Sabe as tags que você define no frontmatter do arquivo markdown? Então, este plugin permite ter uma página para exibir todas as tags utilizadas (layout "FrontmatterKey.vue") e visualizar todos os artigos de uma tag (layout "FrontmatterPagination.vue").

Caso algum desses layouts específicos não seja encontrado o plugin vai carregar o arquivo "Layout.vue" como fallback. Além disso, vale ressaltar que todos esses layouts serão carregados dentro da estrutura do "GlobalLayout.vue".

Enhance App

Se você precisar adicionar alguma propriedade no prototype do Vue.js (algo bem comum em plugins), é só você criar o arquivo "docs/.vuepress/theme/enhanceApp.js". Para mais detalhes clique aqui.

Distribuindo o Tema

Neste artigo colocamos todos os arquivos do tema dentro da pasta "docs/.vuepress/theme". Isso somente é recomendado para ambiente desenvolvimento ou para casos que não deseje distribuí-lo para outros sites.

Mas se você deseja reutilizar o tema em outros sites, você deve criar um projeto separado para ele e publicá-lo preferencialmente no NPM. A partir disso outros projetos podem utilizá-lo dessa forma:

// docs/.vuepress/config.js
module.exports = {
  title: 'My Site',
  theme: 'vuepress-theme-NAMEOFTHEME',
  themeConfig: {},
};

Importante destacar que existe uma convenção que define que o nome de todos os temas para Vuepress devem possuir o prefixo "vuepress-theme-". Para mais detalhes clique aqui.

Inclusive este blog foi construído com Vuepress e eu criei meu próprio tema. Se quiser utilizá-lo é só acessar este link do NPM. Você pode ver o código deste tema por meio deste repositório no GitHub, uma vez que ele é open source.

Por fim, quero mencionar que neste artigo eu utilizei o Vuepress na versão 1.4 e @vuepress/plugin-blog na versão 1.9.

Dúvidas ou sugestões é só entrar em contato. Abraço.

Autor
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." Martin Fowler