Skip to content

3. Uma primeira aplicação [nuxt.js]

3.1. Criação da aplicação

Para nossos desenvolvimentos [nuxt.js], continuamos a utilizar o VS Code. Criamos uma pasta [dvp] vazia, na qual colocaremos nossos exemplos. Em seguida, abrimos essa pasta:

Image

Salvamos a área de trabalho com o nome [intro-nuxtjs] [3-5]:

Image

Abrimos um terminal [6-7]:

Image

Até agora, utilizamos o gerenciador de pacotes JavaScript [npm]. Para variar, vamos usar aqui o gerenciador [yarn]. Ele vem instalado, assim como o [npm], nas versões mais recentes do [node.js]. Para criar uma primeira aplicação [nuxt], usamos o comando [yarn create nuxt-app <dossier>] [1]. O comando solicitará algumas informações sobre o projeto a ser gerado e, após obtê-las, irá gerá-lo [2]:

Image

No [2], foi criada toda uma árvore de arquivos. O arquivo [package.json] apresenta a lista das bibliotecas JavaScript baixadas na pasta [node-modules] [4]:


{
  "name": "nuxt-intro",
  "version": "1.0.0",
  "description": "nuxt-intro",
  "author": "serge-tahe",
  "private": true,
  "scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore ."
  },
  "dependencies": {
    "nuxt": "^2.0.0",
    "bootstrap-vue": "^2.0.0",
    "bootstrap": "^4.1.3",
    "@nuxtjs/axios": "^5.3.6"
  },
  "devDependencies": {
    "@nuxtjs/eslint-config": "^1.0.1",
    "@nuxtjs/eslint-module": "^1.0.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^6.1.0",
    "eslint-plugin-nuxt": ">=0.4.2",
    "eslint-config-prettier": "^4.1.0",
    "eslint-plugin-prettier": "^3.0.1",
    "prettier": "^1.16.4"
  }
}

Esse arquivo reflete as respostas fornecidas ao comando [create nuxt-app] para definir o projeto criado (novembro de 2019). O leitor pode ter um arquivo [package.json] diferente:

  • pode ter fornecido respostas diferentes às perguntas;
  • o comando [create nuxt-app] terá sofrido alterações desde a redação deste documento: as dependências e as versões terão mudado;

A linha 8 do script é o comando que inicia o aplicativo:

Image

  • no [4], vemos que o aplicativo está disponível no URL e no [localhost:3000];
  • em [5-6], vemos que o aplicativo gera um servidor [6] e um cliente (desse servidor) [5];

Vamos acessar o URL [http://localhost:3000/] em um navegador:

Image

3.2. Descrição da estrutura de uma aplicação [nuxt]

Vamos rever a estrutura da aplicação criada:

Image

A função das pastas é a seguinte:

assets
recursos não compilados do aplicativo (imagens, etc.);
static
os arquivos desta pasta estarão disponíveis na raiz do aplicativo. Nesta pasta, colocam-se os arquivos que devem ser encontrados na raiz do aplicativo, como, por exemplo, o arquivo [robots.txt] destinado aos mecanismos de busca;
components
os componentes [vue] do aplicativo utilizados nos [layouts] e nos [pages];
layouts
os componentes [vue] do aplicativo que servem como layout dos [pages];
páginas
os componentes [vue] exibidos pelas diferentes rotas do aplicativo. Poderíamos chamá-los de visualizações do aplicativo. As páginas desempenham um papel específico no [nuxt]: as rotas são criadas dinamicamente a partir da árvore de diretórios encontrada na pasta [pages];
middleware
os scripts executados a cada mudança de rota. Eles permitem controlar essas rotas;
plug-ins
tem um nome que pode causar confusão. Pode conter plugins, mas também scripts convencionais. Os scripts encontrados nessa pasta são executados ao iniciar o aplicativo;
store
se contiver um script [index.js], este define uma instância do store de [Vuex];

Se uma pasta estiver vazia, ela pode ser excluída da árvore de pastas. No exemplo acima, as pastas [assets, static, middleware, plugins, store] e [2] podem ser excluídas.

3.3. O arquivo de configuração [nuxt.config]

A execução do aplicativo é controlada pelo seguinte arquivo [nuxt.config.js]:


export default {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  head: {
    title: process.env.npm_package_name || '',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
        hid: 'description',
        name: 'description',
        content: process.env.npm_package_description || ''
      }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
  },
  /*
   ** Customize the progress-bar color
   */
  loading: { color: '#fff' },
  /*
   ** Global CSS
   */
  css: [],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [],
  /*
   ** Nuxt.js dev-modules
   */
  buildModules: [
    // Doc: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Documentação: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios'
  ],
  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {},
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  }
}
  • linha 2: o tipo de aplicativo gerado:
    • [universal]: aplicativo cliente/servidor. No carregamento inicial do aplicativo, bem como a cada atualização da página no navegador, o servidor é solicitado a fornecer a página;
    • [sap]: aplicativo do tipo [Single Page Application]: um servidor fornece inicialmente a totalidade do aplicativo. Em seguida, o cliente opera sozinho, mesmo em caso de atualização de uma página no navegador;
  • linhas 6-18: definem o cabeçalho HTML <head> das diferentes páginas do aplicativo:
    • linha 7: a tag <title> do título das páginas;
    • linhas 8-16: as tags <meta>;
    • linha 17: as tags <link>

No aplicativo gerado, a tag <head> é a seguinte (código-fonte da página exibida no navegador):


<title>nuxt-intro</title>
<meta data-n-head="ssr" charset="utf-8">
<meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
<meta data-n-head="ssr" data-hid="description" name="description" content="nuxt-intro">
<link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
<link rel="preload" href="/_nuxt/runtime.js" as="script">
<link rel="preload" href="/_nuxt/commons.app.js" as="script">
<link rel="preload" href="/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/_nuxt/app.js" as="script">

Agora, vamos modificar o arquivo [nuxt.config] da seguinte maneira:


head: {
    title: 'Introduction à [nuxt.js]',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      {
        hid: 'description',
        name: 'description',
        content: 'ssr routing loading asyncdata middleware plugins store'
      }
    ],
    link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
},

Quando executamos a aplicação novamente, a tag <head> passa a ter a seguinte forma (código-fonte da página exibida no navegador):


  <head >
    <title>Introduction à [nuxt.js]</title>
    <meta data-n-head="ssr" charset="utf-8">
    <meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1">
    <meta data-n-head="ssr" data-hid="description" name="description" content="ssr routing loading asyncdata middleware plugins store">
    <link data-n-head="ssr" rel="icon" type="image/x-icon" href="/favicon.ico">
    <link rel="preload" href="/_nuxt/runtime.js" as="script">
    <link rel="preload" href="/_nuxt/commons.app.js" as="script">
    <link rel="preload" href="/_nuxt/vendors.app.js" as="script">
<link rel="preload" href="/_nuxt/app.js" as="script">

Voltemos ao arquivo [nuxt.config]:


export default {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  head: {
    ...
  },
  /*
   ** Customize the progress-bar color
   */
  loading: { color: '#fff' },
  /*
   ** Global CSS
   */
  css: [],
  /*
   ** Plugins to load before mounting the App
   */
  plugins: [],
  /*
   ** Nuxt.js dev-modules
   */
  buildModules: [
    // Doc: https://github.com/nuxt-community/eslint-module
    '@nuxtjs/eslint-module'
  ],
  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://bootstrap-vue.js.org
    'bootstrap-vue/nuxt',
    // Documentação: https://axios.nuxtjs.org/usage
    '@nuxtjs/axios'
  ],
  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {},
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  }
}
  • linha 12: entre cada rota do cliente [nuxt], uma barra de carregamento (loading) aparece se a mudança de rota demorar um pouco. A propriedade [loading] permite configurar essa barra de carregamento, neste caso, a cor da barra;
  • linha 16: os arquivos globais [css]. Eles serão incluídos automaticamente em todas as páginas do aplicativo;
  • linhas 24-27: os módulos JavaScript necessários para a compilação (build) do aplicativo;
  • linhas 31-36: os módulos JavaScript utilizados pelo aplicativo;
  • linha 41: configuração da biblioteca [axios] quando esta foi selecionada pelo usuário para as caixas de diálogo HTTP com servidores de terceiros;
  • linhas 45-50: configuração da compilação (build) do projeto;

É possível adicionar outras chaves ao arquivo de configuração. É possível, por exemplo, definir a porta do serviço (3000 por padrão) e a raiz do projeto (por padrão, a pasta raiz do projeto). É isso que faremos agora, adicionando as seguintes chaves:


// diretório do código-fonte
  srcDir: '.',
  router: {
    // URL raiz das páginas do aplicativo
    base: '/nuxt-intro/'
  },
  // servidor
  server: {
    // porta do serviço — padrão 3000
    port: 81,
    // endereços de rede monitorados — por padrão, localhost=127.0.0.1
    host: '0.0.0.0'
  }
  • linha 2: onde encontrar o código-fonte do projeto. Ele está aqui na pasta atual, ou seja, no mesmo nível do arquivo [nuxt.config.js]. Esse é o valor padrão;
  • linhas 8-13: configuram o servidor (não se esqueça de que um aplicativo [nuxt] do tipo [universal] está instalado tanto no servidor quanto no navegador cliente desse servidor);
  • linha 10: as páginas do aplicativo serão servidas na porta 81 do servidor;
  • linha 12: por padrão, [localhost] (endereço de rede 127.0.0.1). Uma máquina pode ter vários endereços de rede se pertencer a várias redes. O endereço 0.0.0.0 indica que o servidor web escuta todos os endereços de rede da máquina;
  • linhas 3-6: configuram o roteador do aplicativo [nuxt];
  • linha 5: as páginas do aplicativo estarão disponíveis no URL e no [http://localhost:81/nuxt-intro/];

Vamos adicionar essas linhas ao arquivo [nuxt.config.js] e, em seguida, executar o projeto (script npm dev). O resultado é o seguinte:

Image

  • em [1], o endereço da máquina em uma rede pública;
  • em [2], a porta do serviço;
  • em [3], a raiz do aplicativo;

3.4. A pasta [layouts]

Image

A pasta [layouts] destina-se aos componentes de layout. Por padrão, é utilizado o componente denominado [default.vue]. Neste projeto, ele é o seguinte:


<template>
  <div>
    <nuxt />
  </div>
</template>

<style>
html {
  font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI',
    Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-size: 16px;
  word-spacing: 1px;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: border-box;
  margin: 0;
}

.button--green {
  display: inline-block;
  border-radius: 4px;
  border: 1px solid #3b8070;
  color: #3b8070;
  text-decoration: none;
  padding: 10px 30px;
}

.button--green:hover {
  color: #fff;
  background-color: #3b8070;
}

.button--grey {
  display: inline-block;
  border-radius: 4px;
  border: 1px solid #35495e;
  color: #35495e;
  text-decoration: none;
  padding: 10px 30px;
  margin-left: 15px;
}

.button--grey:hover {
  color: #fff;
  background-color: #35495e;
}
</style>

Comentários

  • linhas 1-5: o [template] do componente;
  • linha 3: a tag <nuxt /> indica a página atual do roteamento;
  • linhas 7-55: o estilo incorporado pelo componente de layout. Como ele contém a página atual do roteamento, esse estilo será aplicado a todas as páginas roteadas do aplicativo;

Vemos que o objetivo principal da página [default.vue] aqui é aplicar um estilo às páginas roteadas.

3.5. A pasta [pages]

Image

A pasta [pages] contém as visualizações roteadas, ou seja, aquelas que o usuário vê. A página [index.vue] é a página inicial do aplicativo. No caso de [nuxt.js], não há arquivo de roteamento. As rotas são determinadas a partir da estrutura da pasta [pages]. Nesse caso, a presença de um arquivo [index.vue] criará automaticamente uma rota chamada [index] e um caminho [/index], reduzido para [/], uma vez que se trata da página inicial. Assim, a seguinte rota é criada:

        { name : ‘index’, path : ‘/’}

O arquivo [index.vue] é o seguinte:


<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        nuxt-intro
      </h1>
      <h2 class="subtitle">
        nuxt-intro
      </h2>
      <div class="links">
        <a href="https://nuxtjs.org/" target="_blank" class="button--green">
          Documentation
        </a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >
          GitHub
        </a>
      </div>
    </div>
  </div>
</template>

<script>
import Logo from '~/components/Logo.vue'

export default {
  components: {
    Logo
  }
}
</script>

<style>
.container {
  margin: 0 auto;
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}

.title {
  font-family: 'Quicksand', 'Source Sans Pro', -apple-system, BlinkMacSystemFont,
    'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  display: block;
  font-weight: 300;
  font-size: 100px;
  color: #35495e;
  letter-spacing: 1px;
}

.subtitle {
  font-weight: 300;
  font-size: 42px;
  color: #526488;
  word-spacing: 5px;
  padding-bottom: 15px;
}

.links {
  padding-top: 15px;
}
</style>

As linhas 1 a 25 do arquivo [template] exibem a seguinte visualização:

Image

A imagem [1] é gerada pela linha 4 do [template]. Vemos, portanto, que a página utiliza um componente chamado [logo]. Este está definido nas linhas 27 a 35 do script da página. Na linha 28, a notação [~] designa a raiz do projeto.

Image

O componente [Logo.vue] é o seguinte:


<template>
  <div class="VueToNuxtLogo">
    <div class="Triangle Triangle--two" />
    <div class="Triangle Triangle--one" />
    <div class="Triangle Triangle--three" />
    <div class="Triangle Triangle--four" />
  </div>
</template>

<style>
.VueToNuxtLogo {
  display: inline-block;
  animation: turn 2s linear forwards 1s;
  transform: rotateX(180deg);
  position: relative;
  overflow: hidden;
  height: 180px;
  width: 245px;
}

.Triangle {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
}

.Triangle--one {
  border-left: 105px solid transparent;
  border-right: 105px solid transparent;
  border-bottom: 180px solid #41b883;
}

.Triangle--two {
  top: 30px;
  left: 35px;
  animation: goright 0.5s linear forwards 3.5s;
  border-left: 87.5px solid transparent;
  border-right: 87.5px solid transparent;
  border-bottom: 150px solid #3b8070;
}

.Triangle--three {
  top: 60px;
  left: 35px;
  animation: goright 0.5s linear forwards 3.5s;
  border-left: 70px solid transparent;
  border-right: 70px solid transparent;
  border-bottom: 120px solid #35495e;
}

.Triangle--four {
  top: 120px;
  left: 70px;
  animation: godown 0.5s linear forwards 3s;
  border-left: 35px solid transparent;
  border-right: 35px solid transparent;
  border-bottom: 60px solid #fff;
}

@keyframes turn {
  100% {
    transform: rotateX(0deg);
  }
}

@keyframes godown {
  100% {
    top: 180px;
  }
}

@keyframes goright {
  100% {
    left: 70px;
  }
}
</style>

Esse componente é composto essencialmente por estilos e animações para criar uma imagem animada.

3.7. Visualização DevTools

[Vue DevTools] é a extensão do navegador que permite inspecionar os objetos [nuxt.js] e [vue.js] no navegador. Já a utilizamos no capítulo sobre o [vue.js]. Vamos examinar o que essa ferramenta encontra quando a página inicial do nosso aplicativo é exibida:

Image

  • em [1], o componente [PagesIndex] refere-se à página [pages/index.vue];
  • vemos em [2] que esse componente possui uma propriedade [$route], que é a rota que levou à página [index];

Como um exercício simples, vamos exibir essa rota no console.

3.8. Alteração da página inicial

Vamos modificar o arquivo [index.vue]. Em nossa instalação do projeto, instalamos duas dependências:

  • [eslint]: que verifica a sintaxe dos arquivos JavaScript e dos componentes Vue. Se a extensão [ESLint] do VSCode tiver sido instalada, essa sintaxe é verificada durante a digitação dos textos e os erros são sinalizados imediatamente;
  • [prettier]: que formata os códigos JavaScript de maneira padronizada;

Essas dependências estão registradas no arquivo [package.json]:


"devDependencies": {
    "@nuxtjs/eslint-config": "^1.0.1",
    "@nuxtjs/eslint-module": "^1.0.0",
    "babel-eslint": "^10.0.1",
    "eslint": "^6.1.0",
    "eslint-config-prettier": "^4.1.0",
    "eslint-plugin-nuxt": ">=0.4.2",
    "eslint-plugin-prettier": "^3.0.1",
    "prettier": "^1.16.4"
}

Percebi (novembro de 2019) que, com a instalação feita pelo comando [yarn create nuxt-app], as ferramentas [eslint, prettier] não funcionam durante a digitação dos textos. Os erros só são sinalizados na compilação. Após algumas pesquisas, encontrei uma configuração que funciona:

Image

Instala-se, na raiz do projeto, uma pasta [.vscode] contendo o seguinte arquivo [settings.json]:


{
  "eslint.validate": [
    {
      "language": "vue",
      "autoFix": true
    },
    {
      "language": "javascript",
      "autoFix": true
    }
  ],
  "eslint.autoFixOnSave": true,
  "editor.formatOnSave": false
}
  • linhas 2-11: indicam que, quando o [eslint] valida os arquivos .vue e .js, ele deve corrigir os erros que for possível corrigir;
  • linha 12: quando um arquivo é salvo, o [eslint] deve corrigir os erros que puder corrigir;
  • linha 13: desativa a formatação padrão feita pelo VSCode durante um salvamento. Será o [prettier] que fará isso;

Com essa configuração:

  • os erros de sintaxe ou de formatação são sinalizados assim que o texto é digitado;
  • os erros de formatação são corrigidos automaticamente ao salvar o arquivo;

A biblioteca [prettier] é configurada pelo arquivo [.prettierrc]:

Image

Por padrão, esse arquivo é o seguinte:


{
  "semi": false,
  "arrowParens": "always",
  "singleQuote": true
}
  • linha 1: sem ponto-e-vírgula no final das instruções;
  • linha 2: se uma função “seta” (arrow) tiver um único parâmetro, este deve estar entre parênteses;
  • linha 3: as cadeias de caracteres são colocadas entre apóstrofos (sem aspas);

Adicionamos as duas regras a seguir:


{
  "semi": false,
  "arrowParens": "always",
  "singleQuote": true,
  "printWidth": 120,
  "endOfLine": "auto"
}
  • linha 5: a linha de código pode ter até 120 caracteres;
  • linha 6: o marcador de fim de linha pode ser tanto CRLF (Windows) quanto LF (Unix);

Por fim, o arquivo [package.json] é modificado da seguinte forma:


"scripts": {
    "dev": "nuxt",
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate",
    "lint": "eslint --ext .js,.vue --ignore-path .gitignore .",
    "lintfix": "eslint --fix --ext .js,.vue --ignore-path .gitignore ."
},
  • linha 7: adicionamos o comando [lintfix], que é idêntico ao comando [lint] da linha 6, exceto pelo fato de possuir adicionalmente o parâmetro [--fix]. O comando [lint] verifica a sintaxe e o formato de todos os arquivos do projeto e sinaliza qualquer erro. [lintfix] fará o mesmo, com a diferença de que os problemas de formatação que puderem ser corrigidos serão corrigidos automaticamente. [lintfix] será o comando a ser usado se a compilação falhar devido a problemas de formatação dos arquivos;

Feito isso, modificamos o arquivo [index.vue] da seguinte maneira:

Image


<script>
/* eslint-disable no-console */
import Logo from '~/components/Logo.vue'

export default {
  components: {
    Logo
  },
  // ciclo de vida
  created() {
    console.log('created, route=', this.$route)
  }
}
</script>
  • linhas 10-12: adicionamos a função [created], que é executada automaticamente quando o componente é criado;
  • linha 11: exibe-se a rota atual;
  • linha 2: um comentário destinado à função [eslint]. Sem esse comentário, a função [eslint] sinaliza um erro linha 11: não são permitidas instruções [console] nas funções do ciclo de vida. O [eslint] é configurável. Manteremos sua configuração padrão e usaremos comentários como o da linha 2 para desativar uma regra específica do [eslint]. Usaremos dois tipos de comentários:
    • /* desativação da regra [eslint] */: desativação de uma regra para todo o arquivo;
    • // desativação da regra [eslint]: desativação de uma regra para a linha seguinte;

Durante a digitação, os erros são sinalizados e uma função [Quick Fix] está disponível:

Image

Executa-se o projeto:

Image

  • em [1], a aba [Vue] das ferramentas de desenvolvimento do navegador (F12);
  • em [2] e [3], a exibição do trajeto;

Por que duas visualizações e não apenas uma?

Um aplicativo [nuxt] é composto por dois elementos: um servidor e um cliente:

  1. o servidor fornece as páginas da aplicação ao iniciá-la e, posteriormente, sempre que uma página é atualizada no navegador (F5) ou quando o usuário digita manualmente um URL da aplicação;
  2. cada página fornecida pelo navegador contém a página solicitada, bem como o código JavaScript de toda a aplicação, que é então executado no navegador. Esse é o cliente. Enquanto não houver atualização da página no navegador, o aplicativo funciona como um aplicativo Vue clássico no modo [sap] (Aplicativo de Página Única). Assim que o usuário provoca manualmente uma atualização da página, esta é solicitada ao servidor e voltamos à fase 1 anterior.

O que é preciso entender é que são as mesmas páginas do arquivo [pages] que são fornecidas pelo servidor ou pelo cliente. Por esse motivo, os desenvolvedores do [nuxt] chamam esse tipo de página de páginas isomórficas. As mesmas páginas [.vue] podem ser interpretadas tanto pelo cliente quanto pelo servidor. Tomemos como exemplo a página [index]:


<template>
  <div class="container">
    <div>
      <logo />
      <h1 class="title">
        nuxt-intro
      </h1>
      <h2 class="subtitle">
        nuxt-intro
      </h2>
      <div class="links">
        <a href="https://nuxtjs.org/" target="_blank" class="button--green">
          Documentation
        </a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >
          GitHub
        </a>
      </div>
    </div>
  </div>
</template>

<script>
/* eslint-disable no-console */
import Logo from '~/components/Logo.vue'

export default {
  components: {
    Logo
  },
  // ciclo de vida
  created() {
    console.log('created, route=', this.$route)
  }
}
</script>

Como se trata da página inicial, ao iniciar o aplicativo ela é servida pelo servidor. A página no servidor também possui um ciclo de vida, o mesmo de uma página [Vue] clássica, exceto pelas funções [beforeMount, monted], que não existem no lado do servidor. A função [created] é executada, o que explica o primeiro registro. Isso significa, aliás, que o servidor é capaz de executar scripts JavaScript. Neste caso e, em geral, esse servidor é um servidor [node.js]. Depois que a página é criada no servidor, ela chega ao navegador, onde passa novamente pelo ciclo de vida. A função [created] é executada uma segunda vez, o que gera o segundo registro.

A arquitetura de um aplicativo [nuxt] poderia ser a seguinte:

Image

  • [1]: o navegador que hospeda a aplicação [nuxt] quando esta foi carregada no navegador. É o que chamamos de cliente [nuxt];
  • [3]: o servidor que hospeda inicialmente o aplicativo [nuxt]. Este é carregado no navegador [1] ao iniciar o aplicativo e sempre que o usuário atualiza a página atual do navegador ou digita manualmente um URL do aplicativo. É aí que reside a diferença de funcionamento em relação a um aplicativo Vue clássico. Nesta aplicação, uma vez carregada no navegador, o servidor não era mais consultado posteriormente. Outra diferença importante que ainda não pudemos observar é que o servidor de um aplicativo Vue é um servidor estático, incapaz de interpretar as páginas [.vue], enquanto o de um aplicativo Nuxt do tipo [universal] é um servidor JavaScript. Antes de enviar uma página ao navegador, o servidor pode executar scripts e, por exemplo, buscar dados no servidor [2];
  • [2]: é o servidor que fornece dados tanto para os clientes [nuxt] e [1] quanto para os servidores [nuxt] e [3];

No esquema acima, é possível distinguir três subsistemas cliente/servidor:

  • [1, 3]: hospeda o aplicativo [nuxt]. [3] fornece-a ao iniciar o aplicativo com a página inicial e sempre que o usuário solicita uma página manualmente. [1] hospeda oaplicativo [nuxt] recebido de [3], que opera no modo [SAP] enquanto as páginas não forem solicitadas manualmente ao [3];
  • [1, 2]: no modo [SAP], o cliente [nuxt] recupera dados externos de um ou mais servidores;
  • [3, 2]: durante a geração da página solicitada pelo usuário, o servidor [3] também pode recuperar dados externos de um ou mais servidores;

Portanto, é o servidor [3] que distingue uma aplicação [nuxt] de uma aplicação [vue]. Esse servidor é acionado sempre que o usuário solicita uma página manualmente. Ele processa as mesmas páginas [.vue] que os clientes [vue] e [1]. Trata-se de um servidor JavaScript capaz de executar os scripts presentes na página. Isso pode alterar, por exemplo, a forma como a página inicial é gerada com dados externos: enquanto que uma aplicação [vue] obtém esses dados necessariamente do cliente [1], aqui eles podem ser obtidos pelo servidor [3] antes que a página seja enviada ao cliente. A página inicial torna-se, assim, significativa e pode contribuir para melhorar o SEO do aplicativo.

Observação: no modo de desenvolvimento, as três entidades [1, 2, 3] geralmente estão na mesma máquina. Esse será o caso aqui para todos os nossos exemplos.

3.9. Mudança do código-fonte do aplicativo para uma pasta separada

Posteriormente, criaremos diversas aplicações [nuxt] na mesma pasta [dvp]. De fato, a pasta de dependências [node_modules] gerada para cada projeto [nuxt] pode chegar a várias centenas de megabytes. Vamos criar várias pastas [nuxt-00, nuxt-01, ...] dentro da pasta [dvp] para armazenar o código-fonte dos exemplos a serem testados. Em seguida, usaremos o arquivo de configuração [nuxt-config.js] para indicar onde está o código-fonte do projeto [dvp], que continuará sendo o único projeto [nuxt] deste tutorial.

Vamos mover o código-fonte do aplicativo gerado inicialmente pelo comando [yarn create nuxt-app] para uma pasta [nuxt-00]:

Image

  • em [2], movemos as pastas [components, layouts, pages] para uma pasta [nuxt-00];
  • no [3], precisamos modificar o arquivo [nuxt.config.js];

Modificamos o arquivo [nuxt.config.js] da seguinte maneira:


export default {
  mode: 'universal',
  /*
   ** Headers of the page
   */
  ...
  /*
   ** Build configuration
   */
  build: {
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  },
  // diretório do código-fonte
  srcDir: 'nuxt-00',
  // roteador
  router: {
    // raiz dos URL do aplicativo
    base: '/nuxt-00/'
  },
  // servidor
  server: {
    // porta de serviço, 3000 por padrão
    port: 81,
    // endereços de rede monitorados, por padrão localhost: 127.0.0.1
    // 0.0.0.0 = todos os endereços de rede da máquina
    host: '0.0.0.0'
  }
}

O arquivo é alterado em dois pontos:

  • linha 17: indicamos que o código-fonte do projeto [dvp] se encontra na pasta [nuxt-00];
  • linha 21: indica-se que a raiz da aplicação URL passa a ser [/nuxt-00/]. Essa alteração não era obrigatória. Seria possível não definir essa propriedade, e a raiz do URL seria, então, [/]. Aqui, isso nos permitirá lembrar que o código-fonte executado é o da pasta [nuxt-00];

Feito isso, o projeto [dvp] é executado como anteriormente:

Image

3.10. Implantação do aplicativo [nuxt-00]

Vamos executar a aplicação [nuxt-00] em um ambiente diferente do ambiente integrado da VSCode.

Primeiro, compilamos a aplicação:

Image

  • em [3], o resultado da compilação do cliente. Será executado pelo navegador;
  • em [4], o resultado da compilação do servidor. Será executado pelo servidor [node.js];

O resultado da compilação é colocado na pasta [.nuxt]:

Image

Copiamos as pastas [.nuxt, node_modules] e os arquivos [package.json, nuxt.config.js] para uma pasta separada:

Image

O arquivo [package.json] é simplificado da seguinte forma:


{
  "scripts": {
    "start": "nuxt start"
  }
}
  • mantém-se apenas o script [start], que permite executar a versão compilada do projeto;

O arquivo [nuxt.config.js] é simplificado da seguinte forma:


export default {
  // roteador
  router: {
    // raiz dos URL do aplicativo
    base: '/nuxt-00/'
  },
  // servidor
  server: {
    // porta de serviço, 3000 por padrão
    port: 81,
    // endereços de rede monitorados, por padrão localhost: 127.0.0.1
    // 0.0.0.0 = todos os endereços de rede da máquina
    host: '0.0.0.0'
  }
}
  • linha 5: define-se o URL básico do aplicativo compilado;
  • linhas 8-14: define-se a porta de serviço e os endereços de rede que serão monitorados;

Feito isso, abre-se um terminal Laragon e navega-se até a pasta que contém a versão compilada do projeto. É possível abrir qualquer tipo de terminal, mas o executável [npm] deve estar no diretório do terminal. Esse é o caso do terminal Laragon.

Feito isso, digite o comando [npm run start]:

Image

Em [3], vemos que um servidor foi iniciado e que ele está escutando no endereço URL [http://192.168.1.128:81/nuxt-00/]. Agora, vamos acessar esse endereço URL com um navegador [4]. O resultado é exatamente o mesmo de antes. No terminal, foram gravados logs [5]. Trata-se do log inserido no método [created] da página [index.vue], que foi executada pelo servidor [node.js].

Image

No lado do navegador [6], também encontramos o log do método [created] da página [index.vue], mas executado, desta vez, pelo cliente.

3.11. Configuração de um servidor seguro

Acima, o URL do aplicativo é [http://192.168.1.128/nuxt-00/]. Gostaríamos que fosse [https://192.168.1.128/nuxt-00/]. Portanto, precisamos construir um servidor seguro. Mostramos como proceder.

Observação: o método foi extraído do artigo [https://stackoverflow.com/questions/56966137/how-to-run-nuxt-npm-run-dev-with-https-in-localhost].

Primeiro, criamos uma chave privada e uma chave pública com o comando [openssl]. O [openssl] normalmente é instalado junto com o servidor Laragon. Assim, esse comando está disponível em qualquer terminal do Laragon. Vamos, então, abrir um terminal do Laragon e navegar até a pasta do aplicativo implantado:

Image

Image

  • no [2], digitamos o comando [openssl genrsa 2048 > server.key];
  • em [3], é criado um arquivo [server.key];
  • em [4], digite o comando [openssl req -new -x509 -nodes -sha256 -days 365 -key server.key -out server.crt];
  • em [5], é criado um arquivo [server.crt];

Esses dois arquivos constituem um certificado autoassinado. A maioria dos navegadores só os aceita após a aprovação do usuário que solicitou a página.

Os arquivos [server.key, server.crt] devem agora ser utilizados pelo aplicativo web. Para isso, o arquivo [nuxt.config.js] deve ser modificado da seguinte forma:


import path from 'path'
import fs from 'fs'

export default {
  // roteador
  router: {
    // raiz dos URL do aplicativo
    base: '/nuxt-00/'
  },
  // servidor
  server: {
    // porta de serviço, 3000 por padrão
    port: 81,
    // endereços de rede monitorados, por padrão localhost: 127.0.0.1
    // 0.0.0.0 = todos os endereços de rede da máquina
    host: '0.0.0.0',
    // certificado autoassinado
    https: {
      key: fs.readFileSync(path.resolve(__dirname, 'server.key')),
      cert: fs.readFileSync(path.resolve(__dirname, 'server.crt'))
    }
  }
}

São as linhas 18 a 21 que implementam o protocolo [https].

Agora, vamos executar novamente a aplicação:

Image

3.12. Fim do primeiro exemplo

O primeiro exemplo está concluído. Ele nos ensinou muitos conceitos do [nuxt]. Agora vamos desenvolver outros exemplos que colocaremos em pastas [nuxt-01, nuxt-02, ...]. Como esses exemplos usarão um arquivo [nuxt.config.js] diferente, salvaremos em cada uma dessas pastas o arquivo [nuxt.config.js] que serviu para executá-los:

Image