Artigo original: The Next.js Handbook – Learn Next.js for Beginners

Escrevi este tutorial para ajudá-lo a aprender Next.js rapidamente e a se familiarizar com o seu funcionamento.

Ele é ideal para aqueles que têm zero ou pouco conhecimento sobre o Next.js, que já tenham usado React no passado e que estão ansiosos para mergulhar mais no ecossistema do React, em especial, na renderização do lado do servidor.

Acho o Next.js uma ferramenta fantástica para criar aplicações para a web. No final deste artigo, espero que vocês estejam tão entusiasmados sobre isso tanto quanto eu. Também espero que ele o ajude a aprender o Next.js!

Observação: você pode baixar uma versão em PDF/ePub/Mobi deste tutorial para ler off-line!

Índice

  1. Introdução
  2. As principais características fornecidas pelo Next.js
  3. Next.js x Gatsby x create-react-app
  4. Como instalar o Next.js
  5. Vendo o código-fonte da página para confirmar se a renderização do lado do servidor está funcionando
  6. Os pacotes da aplicação
  7. O que é esse ícone no canto inferior direito?
  8. Instalando as ferramentas de desenvolvedor do React
  9. Outras técnicas de depuração que você pode usar
  10. Adicionando uma segunda página ao site
  11. Ligando as duas páginas
  12. Conteúdo dinâmico com o roteador
  13. Pré-busca
  14. Usando o roteador para detectar o link ativo
  15. Usando next/router
  16. Enviando dados aos componentes usando getInitialProps()
  17. CSS
  18. Preenchendo a tag head com tags personalizadas
  19. Adicionando um componente wrapper
  20. Rotas de API
  21. Executando o código somente no lado do servidor ou do client
  22. Fazendo o deploy da versão em produção
  23. Fazendo o deploy no Now
  24. Analisando os pacotes da aplicação
  25. Módulos de lazy loading
  26. Para onde ir a partir daqui

Introdução

Trabalhar em uma aplicação moderna em JavaScript, desenvolvida com React, é fantástico até que você perceba que existem alguns problemas relacionados à renderização de todo o conteúdo no lado do client.

Primeiro, a página leva mais tempo para ficar visível para o usuário, porque antes de carregar o conteúdo, todo o JavaScript precisa ser carregado. Além disso, sua aplicação precisa ser executada para determinar o que mostrar na página.

Em segundo, se você está construindo um site publicamente disponível, terá um problema de conteúdo relacionado à SEO. Os motores de busca estão ficando melhores em rodar e indexar aplicações em JavaScript, mas é muito melhor se pudermos enviar para eles o conteúdo em vez de deixá-los descobrir sozinhos.

A solução para esses dois problemas é a renderização do lado do servidor, também chamada de pré-renderização estática.

O Next.js é um framework para o React para fazer tudo isso de modo muito simples, mas não é limitada a isso. Ele é anunciado por seus criadores como um conjunto de ferramentas de comando único que não necessita de configuração para aplicações em React.

Ele fornece uma estrutura comum, que permite que você construa facilmente uma aplicação para front-end em React, e que trata de modo transparente a renderização do lado do servidor para você.

As principais características fornecidas pelo Next.js

Aqui temos uma lista não exaustiva das principais características do Next.js :

Recarregamento rápido de código (hot code reloading)

O Next.js recarrega a página quando detecta qualquer alteração salva em disco.

Roteamento automático

Qualquer URL é mapeado para o sistema de arquivos e para os arquivos colocados na pasta pages. Você não precisa de nenhuma configuração (embora, logicamente, você tenha opções de personalização).

Componentes de arquivo único

Usando o styled-jsx, completamente integrado – já que foi criado pela mesma equipe, é fácil adicionar estilos com escopo definido ao componente.

Renderização do lado do servidor

Você pode renderizar os componentes do React no lado do servidor antes de enviar o HTML para o client.

Compatibilidade de ecossistemas

O Next.js funciona bem com o resto do ecossistema JavaScript, Node e React.

Divisão automática de códigos

As páginas são renderizadas apenas com as bibliotecas e o JavaScript de que elas precisam, e só. Ao invés de gerar um único arquivo JavaScript contendo todo o código da aplicação, ela é dividida automaticamente pelo Next.js em vários recursos diferentes.

O carregamento de uma página busca apenas o JavaScript necessário para aquela página específica.

O Next.js faz isso analisando os recursos importados.

Se apenas uma de suas páginas importar a biblioteca Axios, por exemplo, essa página específica incluirá a biblioteca em seu pacote.

Isso garante que sua primeira página seja carregada o mais rápido possível, e somente os carregamentos futuros da página (se forem acionados em algum momento) enviarão o JavaScript necessário para o client.

Há uma notável exceção. As importações frequentemente usadas são movidas para o pacote principal do JavaScript se forem usadas em, pelo menos, metade das páginas do site.

Pré-busca

O componente Link, usado para unir páginas diferentes, suporta uma propriedade de prefetch, que automaticamente faz a pré-busca dos recursos de página (incluindo o código faltando devido à divisão do código) em segundo plano.

Componentes dinâmicos

Você pode importar módulos do JavaScript e componentes do React dinamicamente.

Exportações estáticas

Usando o comando de next export, o Next.js permite que você exporte um site totalmente estático de sua aplicação.

Suporte a TypeScript

O Next.js é escrito em TypeScript e, como tal, vem com um excelente suporte ao TypeScript.

Next.js x Gatsby x create-react-app

O Next.js, o Gatsby e o create-react-app são ferramentas incríveis que podemos usar para potencializar as nossas aplicações.

Vamos primeiro dizer o que eles têm em comum. Todos eles usam o React internamente, potencializando toda a experiência de desenvolvimento. Eles também abstraem o webpack e todas aquelas coisas de baixo nível que costumávamos configurar manualmente nos bons e velhos tempos.

O create-react-app não ajuda a gerar facilmente uma aplicação renderizada do lado do servidor. Tudo que vem com essa renderização (SEO, velocidade...) é fornecido apenas por ferramentas como o Next.js e o Gatsby.

Quando o Next.js é melhor que o Gatsby?

Os dois podem ajudar com a renderização do lado do servidor, mas de dois modos diferentes.

O resultado final usando o Gatsby é um gerador de site estático, sem um servidor. Você constrói o site e, então, implanta o resultado do processo de criação estaticamente no Netlify ou em outro site de hospedagem estático.

O Next.js fornece um back-end que pode dar uma resposta ao pedido do servidor, permitindo que você crie um site dinâmico, o que significa que você o implantará em uma plataforma onde possa rodar o Node.js.

O Next.js também pode gerar um site estático, mas não diria que é o seu principal caso de uso.

Se o meu objetivo fosse construir um site estático, eu teria dificuldade para escolher – e talvez o Gatsby tenha um ecossistema melhor de plug-ins, incluindo muitos específicos para blogs.

O Gatsby também é fortemente baseado em GraphQL, algo que você pode gostar ou não gostar, dependendo de suas opiniões e necessidades.

Como instalar o Next.js?

Para instalar o Next.js, você precisa ter o Node.js instalado.

Certifique-se de ter a última versão do Node. Verifique isso digitando node -v em seu terminal, e compare sua versão com a última versão LTS listada em https://nodejs.org/.

Após instalar o Node.js, você terá o comando npm disponível em sua linha de comando.

Se você tiver algum problema nessa fase, recomendo os seguintes tutoriais que escrevi para você (em inglês):

Agora que você tem o Node (atualizado com a última versão) e o npm, estamos prontos!

Podemos escolher 2 caminhos agora: usar a abordagem do create-next-app ou a abordagem clássica, que envolve a instalação e a configuração manual de uma aplicação do Next.

Usando o create-next-app

Se você está familiarizado com o create-react-app, o create-next-app é a mesma coisa – exceto pelo fato de ele criar uma aplicação do Next ao invés de uma aplicação do React, como o nome implica.

Presumo que você já tenha instalado o Node.js, que, a partir da versão 5.2 (há mais de dois anos, no momento em que escrevo), vem com o comando npx integrado (texto em inglês). Essa ferramenta útil nos permite baixar e executar um comando do JavaScript. Nós o usaremos assim:

npx create-next-app

O comando pede o nome da aplicação (e cria uma pasta para você com esse nome), depois faz o download de todos os pacotes necessários (react, react-dom, next) e define o package.json deste modo:

Screen-Shot-2019-11-14-at-16.46.47

Você pode executar imediatamente a aplicação de exemplo, com o comando npm run dev:

Screen-Shot-2019-11-14-at-16.46.32

E aqui está o resultado em http://localhost:3000:

Screen-Shot-2019-11-14-at-16.47.17

Esta é a maneira recomendada de se iniciar uma aplicação do Next.js, pois ela oferece a estrutura e o código de exemplo para testarmos. Há mais além dessa aplicação de amostra padrão. Você pode usar qualquer um dos exemplos armazenados em https://github.com/zeit/next.js/tree/canary/examples usando a opção --example. Por exemplo, tente:

npx create-next-app --example blog-starter

Isso dá a você uma instância de blog imediatamente utilizável e com destaque de sintaxe:

Screen-Shot-2019-11-14-at-17.13.29

Criar manualmente uma aplicação do Next.js

Você pode evitar o create-next-app se tiver vontade de criar uma aplicação do Next a partir do zero. É assim: crie uma pasta vazia em qualquer lugar que desejar (por exemplo, em sua pasta pessoal) e entre nela:

mkdir nextjs
cd nextjs

Depois, crie seu primeiro diretório de projeto Next:

mkdir firstproject
cd firstproject

Agora, use o comando npm  para inicializá-lo como um projeto do Node:‌

npm init -y

A opção -y diz ao npm para usar as configurações padrão de um projeto, preenchendo um arquivo de amostra do package.json.

Screen-Shot-2019-11-04-at-16.59.21

Agora, instale o Next e o React:

npm install next react react-dom

Sua pasta de projeto agora terá 2 arquivos:

Ela também terá a pasta node_modules.

Abra a pasta do projeto usando seu editor favorito. Meu editor preferido é o VS Code. Se você tiver o VS Code instalado, pode executar usando code . em seu terminal para abrir a pasta atual no editor (se o comando não funcionar para você, veja isso - instruções em inglês)

Abra o package.json, que agora tem este conteúdo:

{
  "name": "primeiroprojeto",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies":  {
    "next": "^9.1.2",
    "react": "^16.11.0",
    "react-dom": "^16.11.0"
  }
}

Substitua a seção de scripts por:

"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
}

Assim, você adicionará os comandos de construção do Next.js, que vamos usar em breve.

Dica: use "dev": "next -p 3001", para mudar a porta e executar – neste exemplo, na porta 3001.

Screen-Shot-2019-11-04-at-17.01.03

Agora, crie a pasta pages e adicione um arquivo index.js.

Nesse arquivo, vamos criar nosso primeiro componente do React.

Vamos usá-lo como a exportação padrão:

const Index = () => (
  <div>
    <h1>Home page</h1>
  </div>
)

export default Index

Agora, usando o terminal, execute npm run dev para iniciar o servidor de desenvolvimento do Next.

Isso tornará a aplicação disponível na porta 3000, no localhost.

Screen-Shot-2019-11-04-at-11.24.02

Abra http://localhost:3000 no seu navegador para vê-lo.

Screen-Shot-2019-11-04-at-11.24.23

Vendo o código-fonte da página para confirmar que a renderização do lado do servidor está funcionando

Vamos agora verificar se a aplicação está funcionando como esperávamos que funcionasse. É uma aplicação do Next.js. Portanto, deve ser renderizada do lado do servidor.

Esse é um dos principais pontos valorizados do Next.js: se criarmos um site usando o Next.js, as páginas do site serão renderizadas no lado do servidor, que, por sua vez, entrega o HTML para o navegador.

Isso tem três grandes benefícios:

  • O client não precisa instanciar o React para renderizar, o que torna o site mais rápido para os seus usuários.
  • Os motores de busca indexarão as páginas sem a necessidade de executar o JavaScript do lado do client, algo que o Google começou a fazer, mas admitiu abertamente ser um processo mais lento (e você deve ajudar o Google tanto quanto possível se quiser ter uma boa classificação).
  • Você pode ter metatags de mídia social, úteis para adicionar imagens de visualização, personalizar o título e a descrição para qualquer uma de suas páginas compartilhadas no Facebook, Twitter e assim por diante.

Vamos ver o código-fonte da aplicação. Usando o Chrome, você pode clicar com o botão direito em qualquer lugar da página e pressionar View Page Source (Exibir código-fonte da página, em português).

Screen-Shot-2019-11-04-at-11.33.10

Se você visualizar o código-fonte da página, verá o trecho de código da página inicial, <div><h1>Home page</h1></div>, no corpo do HTML, juntamente com vários arquivos JavaScript - os pacotes da aplicação.

Não precisamos configurar nada. A SSR (server-side rendering - ou renderização do lado do servidor) já estará trabalhando para nós.

A aplicação em React será lançada no client e potencializará as interações, como clicar em um link, utilizando a renderização do lado do client. Recarregar uma página, no entanto, ocorrerá a partir do servidor. Usando o Next.js, não deve haver diferença no resultado dentro do navegador - uma página renderizada no servidor deve ser exatamente igual a uma página renderizada no client.

Os pacotes da aplicação

Quando vimos o código-fonte da página, percebemos vários arquivos do JavaScript sendo carregados:

Screen-Shot-2019-11-04-at-11.34.41

Vamos começar colocando o código em um formatador de HTML para que ele seja melhor formatado, para que nós humanos possamos ter uma chance de entendê-lo melhor:

<!DOCTYPE html>
<html>

<head>
    <meta charSet="utf-8" />
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1" />
    <meta name="next-head-count" content="2" />
    <link rel="preload" href="/_next/static/development/pages/index.js?ts=1572863116051" as="script" />
    <link rel="preload" href="/_next/static/development/pages/_app.js?ts=1572863116051" as="script" />
    <link rel="preload" href="/_next/static/runtime/webpack.js?ts=1572863116051" as="script" />
    <link rel="preload" href="/_next/static/runtime/main.js?ts=1572863116051" as="script" />
</head>

<body>
    <div id="__next">
        <div>
            <h1>Home page</h1></div>
    </div>
    <script src="/_next/static/development/dll/dll_01ec57fc9b90d43b98a8.js?ts=1572863116051"></script>
    <script id="__NEXT_DATA__" type="application/json">{"dataManager":"[]","props":{"pageProps":{}},"page":"/","query":{},"buildId":"development","nextExport":true,"autoExport":true}</script>
    <script async="" data-next-page="/" src="/_next/static/development/pages/index.js?ts=1572863116051"></script>
    <script async="" data-next-page="/_app" src="/_next/static/development/pages/_app.js?ts=1572863116051"></script>
    <script src="/_next/static/runtime/webpack.js?ts=1572863116051" async=""></script>
    <script src="/_next/static/runtime/main.js?ts=1572863116051" async=""></script>
</body>

</html>

Temos quatro arquivos JavaScript sendo declarados como pré-carregados no head, usando rel="preload" as="script":

  • /_next/static/development/pages/index.js (96 linhas de código)
  • /_next/static/development/pages/_app.js (5.900 linhas de código)
  • /_next/static/runtime/webpack.js (939 linhas de código)
  • /_next/static/runtime/main.js (12 mil linhas de código)

Isso diz ao navegador para começar a carregar esses arquivos o mais rápido possível, antes que o fluxo normal de renderização comece. Sem eles, os scripts seriam carregados com um atraso adicional. O carregamento rápido desses arquivos melhora o desempenho de carregamento da página.

Esses quatro arquivos são, então, carregados ao final do body, juntamente com o /_next/static/development/dll/dll_01ec57fc9b90d43b98a8.js (31 mil linhas de código) e com um trecho de código em JSON, que estabelece alguns padrões para os dados da página:

<script id="__NEXT_DATA__" type="application/json">
{
  "dataManager": "[]",
  "props": {
    "pageProps":  {}
  },
  "page": "/",
  "query": {},
  "buildId": "development",
  "nextExport": true,
  "autoExport": true
}
</script>

Os quatro pacotes de arquivos carregados já estão implementando um recurso chamado divisão de código. O arquivo index.js fornece o código necessário para o componente index, que serve a rota /. Se tivéssemos mais páginas, teríamos mais pacotes para cada página, que só seriam carregados se necessário - para proporcionar um desempenho mais eficiente para a página.

O que é esse ícone no canto inferior direito?

Você viu aquele pequeno ícone no canto inferior direito da página, que parece um relâmpago?

Screen-Shot-2019-11-04-at-13.21.42

Se você passar o mouse sobre ele, verá que está dizendo "Prerendered page" (Página pré-renderizada, em português):‌

Screen-Shot-2019-11-04-at-13.21.46

Esse ícone, que só é visível no modo de desenvolvimento, é claro, diz que a página se qualifica para a otimização estática automática, o que, basicamente, significa que ela não depende de dados que precisam ser buscados no momento da invocação, podendo ser pré-renderizada e criada como um arquivo HTML estático no momento da build (quando executamos npm run build).

O Next pode determinar isso pela ausência do método getInitialProps() anexado ao componente da página.

Quando esse é o caso, a nossa página pode ser ainda mais rápida, pois será servida estaticamente como um arquivo HTML em vez de passar pelo servidor do Node.js, que gera a saída HTML.

Outro ícone útil que pode aparecer ao lado dele, ou, ao invés dele, em páginas não pré-renderizadas, é um pequeno triângulo animado:

Screen-Shot-2019-11-14-at-14.56.21

Esse é um indicador de compilação. Ele aparece quando você salva uma página e o Next.js está compilando a aplicação antes que a recarregamento rápido do código faça efeito no recarregamento da aplicação automaticamente.

É um modo muito bom de determinar imediatamente se a aplicação já foi compilada. Você também pode testar uma parte dela na qual você estiver trabalhando.

Instalando as ferramentas de desenvolvedor do React

O Next.js tem como base o React. Portanto, uma ferramenta muito útil que precisamos instalar com certeza (se você ainda não o fez) são as ferramentas de desenvolvedor do React.

Disponíveis tanto para o Chrome quanto para o Firefox, as ferramentas de desenvolvedor do React são um instrumento essencial que você pode usar para inspecionar uma aplicação do React.

As ferramentas de desenvolvedor do React não são específicas para o Next.js, mas eu quero apresentá-las porque você pode não estar 100% familiarizado com todas as ferramentas que o React fornece. É melhor analisar um pouco as ferramentas de depuração do que assumir que você já as conhece.

Elas fornecem um inspetor que revela a árvore de componentes do React que compõe sua página. Para cada componente, você pode verificar as props, o state, os hooks e muito mais.

Uma vez instaladas as ferramentas de desenvolvedor do React, você pode abrir as ferramentas de desenvolvedor normais do navegador (no Chrome, clique com o botão direito do mouse na página e depois clique em Inspect – ou Inspecionar, em português) e você encontrará dois novos painéis: Components e Profiler.

Screen-Shot-2019-11-04-at-14.26.12

Se você passar o mouse sobre os componentes, verá que, na página, o navegador selecionará as partes que serão renderizadas por aquele componente.

Se você selecionar qualquer componente na árvore, o painel direito mostrará uma referência ao componente pai e às props passadas para ele:

Screen-Shot-2019-11-04-at-14.27.05

Você pode navegar facilmente clicando próximo aos nomes dos componentes.

Você pode clicar no ícone do olho, na barra de ferramentas das Ferramentas do desenvolvedor, para inspecionar um elemento do DOM. Se você usar o primeiro ícone, aquele com o ícone do mouse (que, convenientemente, fica sob o ícone  semelhante nas ferramentas do desenvolvedor), você pode passar o mouse sobre um elemento na interface do usuário do navegador para selecionar diretamente o componente do React que o renderiza.

Você pode usar o ícone do bug para registrar os dados de um componente no console.

Screen-Shot-2019-11-04-at-14.31.25

Isso é bastante impressionante, pois, uma vez que você tenha os dados impressos ali, poderá clicar com o botão direito em qualquer elemento e pressionar "Store as a global variable" (Armazenar como uma variável global, em português). Por exemplo, aqui, eu fiz isso com a prop do url e pude inspecioná-la no console usando a variável temporária atribuída a ele, temp1:

Screen-Shot-2019-11-04-at-14.40.22

Usando os Source Maps, que são carregados automaticamente pelo Next.js em modo de desenvolvimento, a partir do painel Components, podemos clicar no código com <> e as ferramentas do desenvolvedor mudarão para o painel Source, mostrando-nos o código-fonte do componente:

Screen-Shot-2019-11-04-at-14.41.33

A aba Profiler é ainda mais impressionante. Ela nos permite registrar uma interação na aplicação e ver o que acontece. Ainda não posso mostrar um exemplo, porque são necessários pelo menos dois componentes para criar uma interação e, de momento, temos apenas um. Falarei sobre isso mais tarde.

Screen-Shot-2019-11-04-at-14.42.24

Todas as capturas de tela que fiz e mostrei acima usam o Chrome, mas as ferramentas de desenvolvedor do React funcionam do mesmo modo no Firefox:

Screen-Shot-2019-11-04-at-14.45.20

Outras técnicas de depuração que você pode usar

Além das ferramentas de desenvolvedor do React, que são essenciais para construir uma aplicação do Next.js, quero mostrar dois modos de depurar aplicações do Next.js.

A primeira é, obviamente, usando o console.log() e todas as outras ferramentas da API do Console (texto em inglês). O modo como as aplicações do Next funcionam fará uma registro do console aparecer no console do navegador OU no terminal onde você iniciou o Next, usando npm run dev.

Em especial, se a página for carregada a partir do servidor, quando você apontar o URL para ela, ou pressionar o botão Refresh (Atualizar) ou Cmd/CTRL-R, todos os registros do console acontecem no terminal.

As transições de página subsequentes que acontecem ao clicar no mouse farão com que todo o registro do console aconteça dentro do navegador. Não se esqueça disso caso se surpreenda com a ausência de um registro no console do navegador de início.

Outra ferramenta essencial é a instrução de debugger. Adicionando esta declaração a um componente, o navegador fará uma pausa na renderização da página:

Screen-Shot-2019-11-04-at-15.10.32

Isso é realmente incrível, porque, agora, você pode usar o depurador do navegador para inspecionar valores e executar sua aplicação uma linha de cada vez.

Você também pode usar o depurador do VS Code para depurar o código do lado do servidor. Menciono essa técnica e esse tutorial (em inglês) para que você possa fazer essa configuração.

Adicionando uma segunda página ao site

Agora que temos um bom domínio das ferramentas que podemos usar para nos ajudar a desenvolver as aplicações do Next.js, vamos continuar de onde paramos em nossa primeira aplicação:

Screen-Shot-2019-11-04-at-13.21.42-1

Quero adicionar uma segunda página a esse site, um blog. Ele vai ser servido em /blog e, por enquanto, conterá apenas uma simples página estática, assim como nosso primeiro componente index.js:

Screen-Shot-2019-11-04-at-15.39.40

Depois de salvar o novo arquivo, o processo npm run dev em execução já é capaz de renderizar a página, sem a necessidade de reiniciá-la.

Quando acessamos o URL http://localhost:3000/blog, temos a nova página:

Screen-Shot-2019-11-04-at-15.41.39

Aqui está o que o terminal nos diz:

Screen-Shot-2019-11-04-at-15.41.03

Agora o fato de o URL ser /blog depende apenas do nome do arquivo, e de sua posição na pasta pages.

Você pode criar uma página pages/hey/ho e ela página aparecerá no URL http://localhost:3000/hey/ho.

O que não importa, para fins de URL, é o nome do componente dentro do arquivo.

Tente ver o código-fonte da página. Quando carregada do servidor, ela listará /_next/static/development/pages/blog.js como um dos pacotes carregados, e não /_next/static/development/pages/index.js como na página inicial. Isso porque, graças à divisão automática do código, não precisamos do pacote que serve a página inicial, apenas do pacote que serve a página do blog.

Screen-Shot-2019-11-04-at-16.24.53

Também podemos simplesmente exportar uma função anônima a partir do blog.js:

export default () => (
  <div>
    <h1>Blog</h1>
  </div>
)

Ou, se você preferir uma sintaxe diferente da sintaxe de arrow functions:

export default function() {
  return (
    <div>
      <h1>Blog</h1>
    </div>
  )
}

Ligando as duas páginas

Agora que temos duas páginas, definidas por index.js e blog.js, podemos introduzir os links.

Os links HTML normais dentro das páginas são feitos usando a tag a:

<a href="/blog">Blog</a>

Não podemos fazer isso no Next.js.

Por quê? Tecnicamente podemos, é claro, porque essa é a web e, na web, as coisas nunca se quebram (é por isso que ainda podemos usar a tag <marquee>. No entanto, um dos principais benefícios de usar o Next é que, uma vez que uma página é carregada, as transições para outra página são muito rápidas graças à renderização do lado do client.

Se você usar um link a simples:

const Index = () => (
  <div>
    <h1>Home page</h1>
    <a href='/blog'>Blog</a>
  </div>
)

export default Index

Agora, abra as ferramentas de desenvolvedor e o painel Network, especificamente. Na primeira vez em que carregamos http://localhost:3000/, temos todos os pacotes de páginas carregados:

Screen-Shot-2019-11-04-at-16.26.00

Agora, se você clicar no botão "Preserve log" (para evitar limpar o painel Network), e clicar no link "Blog", isso é o que acontece:

Screen-Shot-2019-11-04-at-16.27.16

Recebemos todo aquele JavaScript do servidor novamente! O problema é que não precisamos de todo aquele JavaScript, pois já o temos. Precisaríamos apenas do pacote de páginas do blog.js, o único que é novo na página.

Para resolver esse problema, usamos um componente fornecido pelo Next, chamado Link.

Importamos o componente assim:

import Link from 'next/link'

Depois, usamos o componente para envolver nosso link com a, assim:

import Link from 'next/link'

const Index = () => (
  <div>
    <h1>Home page</h1>
    <Link href='/blog'>
      <a>Blog</a>
    </Link>
  </div>
)

export default Index

Agora, se você tentar novamente o que fizemos anteriormente, verá que somente o pacote de blog.js é carregado quando nos movemos para a página do blog:

Screen-Shot-2019-11-04-at-16.35.18

A página é carregada mais rápido do que antes. O spinner usual do navegador na aba nem apareceu. No entanto, o URL mudou, como você pode ver. Tudo está funcionando perfeitamente com a API de histórico (texto em inglês) do navegador.

Essa é a renderização do lado do client em ação.

Se você pressionar o botão Voltar agora, nada é carregado, pois o navegador ainda tem o antigo pacote index.js no lugar, pronto para carregar a rota /index. É tudo automático!

Conteúdo dinâmico com o roteador

No capítulo anterior, vimos como vincular a página inicial à página do blog.

Um blog é um grande caso de uso para o Next.js, um que continuaremos a explorar neste capítulo adicionando posts no blog.

As postagens no blog têm um URL dinâmico. Por exemplo, um post intitulado "Olá Mundo" pode ter o URL /blog/ola-mundo. Um post com o título "Meu segundo post" pode ter o URL /blog/meu-segundo-post.

Esse conteúdo é dinâmico e pode ser retirado de um banco de dados, de arquivos de markdown e outros.

O Next.js pode servir conteúdo dinâmico baseado em um URL dinâmico.

Criamos um URL dinâmico usando uma página dinâmica com a sintaxe [].

Como? Adicionamos um arquivo de páginas pages/blog/[id].js. Esse arquivo tratará todos os URLs dinâmicos sob a rota /blog/, como os que mencionamos acima: /blog/ola-mundo, /blog/meu-segundo-post e muito mais.

No nome do arquivo, [id] dentro dos colchetes significa que qualquer coisa que seja dinâmica será colocada dentro do parâmetro id da propriedade de query de router (em português, roteador).

Ok, isso é um pouco de tudo ao mesmo tempo – e pode confundir.

O que é o router? O router é uma biblioteca fornecida pelo Next.js.

Nós a importamos de next/router:

import { useRouter } from 'next/router'

Uma vez que tenhamos useRouter, instanciamos o objeto router usando:

const router = useRouter()

Ao termos esse objeto router, podemos extrair informações a partir dele.

Em particular, podemos obter a parte dinâmica do URL no arquivo [id].js acessando o router.query.id.

A parte dinâmica também pode ser apenas uma parte do URL, como post-[id].js

Portanto, vamos continuar e aplicar todas essas coisas na prática.

Crie o arquivo pages/blog/[id].js:

import { useRouter } from 'next/router'

export default () => {
  const router = useRouter()

  return (
    <>
      <h1>Blog post</h1>
      <p>Post id: {router.query.id}</p>
    </>
  )
}

Agora, se você for à rota http://localhost:3000/blog/test, deverá ver isto:

Screen-Shot-2019-11-05-at-16.41.32

Podemos usar esse parâmetro de id para obter o post a partir de uma lista de posts, por exemplo, em um banco de dados. Para manter as coisas simples, vamos adicionar um arquivo posts.json na pasta raiz do projeto:

{
  "test": {
    "title": "test post",
    "content": "Hey some post content"
  },
  "second": {
    "title": "second post",
    "content": "Hey this is the second post content"
  }
}

Agora podemos importá-la e consultar o post a partir da chave de id:

import { useRouter } from 'next/router'
import posts from '../../posts.json'

export default () => {
  const router = useRouter()

  const post = posts[router.query.id]

  return (
    <>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </>
  )
}

Recarregando a página, devemos ver este resultado:

Screen-Shot-2019-11-05-at-16.44.07

‌Em vez disso, porém, recebemos um erro no console e outro erro no navegador:

Screen-Shot-2019-11-05-at-18.18.17

Por quê? Porque durante a renderização, quando o componente é inicializado, os dados ainda não estão lá. Veremos como fornecer os dados para o componente com getInitialProps na próxima seção.

Por enquanto, adicione a verificação if (!post) return <p></p>  antes de retornar o JSX:

import { useRouter } from 'next/router'
import posts from '../../posts.json'

export default () => {
  const router = useRouter()

  const post = posts[router.query.id]
  if (!post) return <p></p>

  return (
    <>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </>
  )
}

Agora, as coisas devem funcionar. Inicialmente, o componente é renderizado sem as informações dinâmicas do router.query.id. Após a renderização, o Next.js aciona uma atualização com o valor da consulta e a página exibe as informações corretas.

Se você visualizar o código-fonte, verá aquela tag vazia <p> no HTML:

Screen-Shot-2019-11-05-at-18.20.58

Em breve, resolveremos esse problema da não implementação da renderização do lado do servidor. Afinal, isso prejudica tanto os tempos de carregamento para os nossos usuários, quanto SEO e o compartilhamento social, como já discutimos.

Podemos completar o exemplo do blog listando esses posts em pages/blog.js:

import posts from '../posts.json'

const Blog = () => (
  <div>
    <h1>Blog</h1>

    <ul>
      {Object.entries(posts).map((value, index) => {
        return <li key={index}>{value[1].title}</li>
      })}
    </ul>
  </div>
)

export default Blog

Podemos, também, vinculá-los às páginas individuais dos posts, importando o Link do next/link e usando-o dentro do laço dos posts:

import Link from 'next/link'
import posts from '../posts.json'

const Blog = () => (
  <div>
    <h1>Blog</h1>

    <ul>
      {Object.entries(posts).map((value, index) => {
        return (
          <li key={index}>
            <Link href='/blog/[id]' as={'/blog/' + value[0]}>
              <a>{value[1].title}</a>
            </Link>
          </li>
        )
      })}
    </ul>
  </div>
)

export default Blog

Pré-busca

Eu mencionei anteriormente como o componente Link do Next.js pode ser usado para criar links entre duas páginas e que, quando você o usa, o Next.js trata transparentemente o roteamento do front-end para nós. Assim, quando um usuário clica em um link, o front-end se encarrega de mostrar a nova página sem acionar um novo ciclo de solicitação e resposta do client/servidor, como normalmente acontece com páginas da web.

Há outra coisa que o Next.js faz por você quando você usa o Link.

Assim que um elemento envolto no <Link> aparece na viewport (o que significa que é visível para o usuário do site), o Next.js faz a pré-busca do URL para o qual aponta, desde que seja um link local (no seu site), tornando a aplicação muito rápida para o espectador.

Esse comportamento é acionado apenas em modo de produção (falaremos sobre isto em profundidade mais tarde), o que significa que você tem que parar a aplicação se a estiver executando com npm run dev, compilar seu pacote de produção com npm run build e a executar com npm run start em seu lugar.

Usando o inspetor da aba Network nas ferramentas do desenvolvedor, você notará que qualquer link na parte superior na página inicial, ao carregar a página, inicia a pré-busca assim que o evento de load for disparado em sua página (ou seja, é acionada quando a página estiver totalmente carregada, acontecendo após o evento DOMContentLoaded).

Qualquer outra tag Link que não esteja na viewport será pré-buscada quando o usuário rolar até ela.

A pré-busca é automática em conexões de alta velocidade (conexões Wi-fi e 3g+, a menos que o navegador envie o cabeçalho de HTTP Save-Data.

Você pode escolher não usar a pré-busca de instâncias individuais do Link, configurando o prop prefetch como false:‌

<Link href="/a-link" prefetch={false}>
  <a>A link</a>
</Link>

Uma característica muito importante ao trabalhar com links é determinar qual é o URL atual e, em particular, atribuir uma classe ao link ativo, para que possamos dar a ele um estilo diferente dos outros.

Isso é especialmente útil no cabeçalho de seu site, por exemplo.

O componente padrão Link do Next.js, oferecido em next/link, não faz isso automaticamente para nós.

Nós mesmos podemos criar um componente Link e armazená-lo em um arquivo Link.js na pasta components, importando esse componente em vez do padrão next/link.

Nesse componente, vamos primeiro importar React de react, Link de next/link e o hook useRouter de next/router.

Dentro do componente, determinamos se o nome do caminho atual corresponde ao prop href do componente e, se assim for, anexamos a classe selected aos elementos filhos.

Finalmente, retornamos esses filhos com a classe atualizada, usando

React.cloneElement():

import React from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'

export default ({ href, children }) => {
  const router = useRouter()

  let className = children.props.className || ''
  if (router.pathname === href) {
    className = `${className} selected`
  }

  return <Link href={href}>{React.cloneElement(children, { className })}</Link>
}

Usando next/router

Já vimos como usar o componente Link para lidar declarativamente com o roteamento em aplicações do Next.js.

É realmente útil gerenciar o roteamento no JSX, mas, às vezes, é necessário acionar uma mudança programática de roteamento.

Nesse caso, você pode acessar o Router do Next.js diretamente, fornecido no pacote next/router, e chamar seu método push().

Aqui está um exemplo de como acessar o router:

import { useRouter } from 'next/router'

export default () => {
  const router = useRouter()
  //...
}

Uma vez que obtemos o objeto router invocando o useRouter(), podemos usar seus métodos.

Esse é o roteador (router) do lado do client. Portanto, os métodos devem ser usados apenas no código voltado para o front-end. O modo mais fácil de garantir isso é envolvendo as chamadas no hook useEffect() do React ou dentro de componentDidMount() nos componentes com state do React.

Aquele que você provavelmente usará mais serão o push() e o prefetch().

push() nos permite acionar programaticamente uma alteração de URL no front-end:‌

router.push('/login')

prefetch() nos permite fazer programaticamente a pré-busca de um URL, o que é útil quando não temos uma tag Link que lide automaticamente com a pré-busca para nós:‌

router.prefetch('/login')

Exemplo completo:

import { useRouter } from 'next/router'

export default () => {
  const router = useRouter()

  useEffect(() => {
    router.prefetch('/login')
  })
}

Você também pode usar o roteador para ouvir os eventos de mudança de rota.

Enviando dados aos componentes usando getInitialProps

No capítulo anterior, tivemos um problema com a geração dinâmica da página de post, pois o componente exigia alguns dados antecipadamente. Tentamos obter os dados do arquivo JSON, assim:

import { useRouter } from 'next/router'
import posts from '../../posts.json'

export default () => {
  const router = useRouter()

  const post = posts[router.query.id]

  return (
    <>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </>
  )
}

Como resultado, obtivemos este erro:

Screen-Shot-2019-11-05-at-18.18.17-1

Como resolvemos isso e como fazemos a renderização do lado do servidor funcionar para as rotas dinâmicas?

Devemos fornecer o componente com props, usando uma função especial chamada getInitialProps(), que é anexada ao componente.

Para fazer isso, primeiro nomeamos o componente:

const Post = () => {
  //...
}

export default Post

Então, adicionamos a função a ele:

const Post = () => {
  //...
}

Post.getInitialProps = () => {
  //...
}

export default Post

Essa função recebe um objeto como argumento, que contém várias propriedades. Em particular, o que nos interessa agora é que tenhamos o objeto de query, aquele que usamos anteriormente para obter a identificação do post.

Assim, podemos obtê-lo usando a sintaxe de desestruturação do objeto:

Post.getInitialProps = ({ query }) => {
  //...
}

Agora, podemos retornar o post a partir dessa função:

Post.getInitialProps = ({ query }) => {
  return {
    post: posts[query.id]
  }
}

Também podemos remover a importação de useRouter. Obtemos o post por meio da propriedade props passada para o componente Post:

import posts from '../../posts.json'

const Post = props => {
  return (
    <div>
      <h1>{props.post.title}</h1>
      <p>{props.post.content}</p>
    </div>
  )
}

Post.getInitialProps = ({ query }) => {
  return {
    post: posts[query.id]
  }
}

export default Post

Agora, não haverá mais erros. A SSR (renderização do lado do servidor) estará funcionando como esperado, como você pode ver verificando a fonte de visualização:

Screen-Shot-2019-11-05-at-18.53.02

A função getInitialProps será executada no lado do servidor, mas também no lado do client quando navegamos para uma nova página usando o componente Link, como fizemos.

É importante notar que getInitialProps tem, no objeto de contexto que recebe, além do objeto de query, essas outras propriedades:

  • pathname: a seção do URL do path
  • asPath: a string do path atual (incluindo a consulta) mostrada no navegador

No caso de chamar  http://localhost:3000/blog/test, isso resultará respectivamente em:

  • /blog/[id]
  • /blog/test

No caso de renderização do lado do servidor, ele também receberá:

  • req: o objeto de solicitação HTTP
  • res: o objeto de resposta HTTP
  • err: um objeto de erro

req e res serão familiares a você se já tiver programado para o Node.js.

CSS

Como estilizar os componentes React no Next.js?

Temos muita liberdade, porque podemos usar a biblioteca que preferirmos.

O Next.js, contudo, vem com o styled-jsx integrado, pois ele é uma biblioteca dos mesmos criadores do Next.js.

Ele é uma biblioteca muito interessante, que nos fornece CSS em escopo, o que é ótimo para a manutenção, pois permite que o CSS afete apenas o componente no qual é aplicado.

Acho que essa é uma ótima abordagem para escrever CSS, sem a necessidade de aplicar bibliotecas adicionais ou pré-processadores adicionais que aumentam a complexidade.

Para adicionar CSS a um componente do React no Next.js, nós o inserimos dentro de um trecho de código no JSX, que começa com

<style jsx>{`

e termina com

`}</style>

Dentro destes blocos estranhos, escrevemos CSS simples, como faríamos em um arquivo .css:

<style jsx>{`
  h1 {
    font-size: 3rem;
  }
`}</style>

Você o escreve dentro do JSX, assim:‌

const Index = () => (
  <div>
		<h1>Home page</h1>

		<style jsx>{`
		  h1 {
		    font-size: 3rem;
		  }
		`}</style>
  </div>
)

export default Index

Dentro do bloco, podemos usar a interpolação para alterar dinamicamente os valores. Por exemplo, aqui assumimos que um prop size está sendo passado pelo componente pai e o usamos no bloco styled-jsx:

const Index = props => (
  <div>
		<h1>Home page</h1>

		<style jsx>{`
		  h1 {
		    font-size: ${props.size}rem;
		  }
		`}</style>
  </div>
)

Se você quiser aplicar algum CSS globalmente, não em escopo a um componente, você adiciona a palavra-chave global à tag de style:

<style jsx global>{`
body {
  margin: 0;
}
`}</style>

Se você quiser importar um arquivo de CSS externo em um componente Next.js, precisa primeiramente instalar o @zeit/next-css:

npm install @zeit/next-css

Depois, é preciso criar um arquivo de configuração na raiz do projeto, chamado next.config.js, com esse conteúdo:‌

const withCSS = require('@zeit/next-css')
module.exports = withCSS()

Após reiniciar a aplicação do Next, você pode importar o CSS como normalmente faz com as bibliotecas do JavaScript ou componentes:

import '../style.css'

Você também pode importar um arquivo SASS diretamente, usando a biblioteca @zeit/next-sass.

Preenchendo a tag head com tags personalizadas

A partir de qualquer componente da página do Next.js, você pode adicionar informações ao cabeçalho da página.

Isso é útil quando:

  • você quer personalizar o título da página
  • você quer mudar uma tag meta

Como você pode fazer isso?

Dentro de cada componente, você pode importar o componente Head do next/head e incluí-lo na saída JSX do seu componente:

import Head from 'next/head'

const House = props => (
  <div>
    <Head>
      <title>The page title</title>
    </Head>
    {/* the rest of the JSX */}
  </div>
)

export default House

Você pode adicionar qualquer tag HTML que gostaria que aparecesse na seção <head> da página.

Ao montar o componente, o Next.js se certificará de que as tags que estão dentro de Head sejam adicionadas ao cabeçalho da página. A mesma coisa ocorre quando da desmontagem do componente. O Next.js se encarregará de remover essas tags.

Adicionando um componente wrapper

Todas as páginas em seu site parecem mais ou menos as mesmas. Há uma janela, uma camada de base comum e você quer apenas mudar o que está dentro.

Há uma barra de navegação, uma barra lateral e, depois, o conteúdo real.

Como você cria um sistema assim no Next.js?

Há dois modos. Um deles é usar um componente de ordem superior (texto em inglês), criando um componente em components/Layout.js:

export default Page => {
  return () => (
    <div>
      <nav>
        <ul>....</ul>
      </hav>
      <main>
        <Page />
      </main>
    </div>
  )
}

Ali, podemos importar componentes separados para o cabeçalho e/ou barra lateral, além de adicionar todo o CSS de que precisamos.

Você poderá usar o CSS em cada página assim:

import withLayout from '../components/Layout.js'

const Page = () => <p>Here's a page!</p>

export default withLayout(Page)

Descobri, porém, que isso funciona apenas para casos simples, onde você não precisa chamar getInitialProps() na página. Por quê?

Porque getInitialProps() só é chamado no componente de página. Mas se exportarmos o componente de ordem superior withLayout() de uma página, Page.getInitialProps() não será chamado. withLayout.getInitialProps() seria.

Para evitar complicar desnecessariamente a nossa base de código, a abordagem alternativa é usar props:

export default props => (
  <div>
    <nav>
      <ul>....</ul>
    </hav>
    <main>
      {props.content}
    </main>
  </div>
)

Em nossas páginas, agora, o usaremos assim:

import Layout from '../components/Layout.js'

const Page = () => (
  <Layout content={(
    <p>Here's a page!</p>
  )} />
)

Essa abordagem nos permite utilizar getInitialProps() de dentro de nosso componente de página, com a única desvantagem de ter de escrever o componente JSX dentro da prop content:‌  

import Layout from '../components/Layout.js'

const Page = () => (
  <Layout content={(
    <p>Here's a page!</p>
  )} />
)

Page.getInitialProps = ({ query }) => {
  //...
}

Rotas de API

Além de criar rotas de páginas, o que significa que as páginas são servidas ao navegador como páginas da web, o Next.js pode criar rotas de API.

Essa é uma característica muito interessante, pois significa que o Next.js pode ser usado para criar um front-end para dados que são armazenados e recuperados pelo próprio Next.js, transferindo o JSON através de pedidos de busca.

As rotas de API vivem sob a pasta /pages/api/ e são mapeadas para o endpoint /api.

Essa característica é muito útil quando criamos aplicações.

Nessas rotas, escrevemos o código em Node.js (ao invés do código em React). É uma mudança de paradigma: você passa do front-end para o back-end, sem problemas.

Digamos que você tenha um arquivo /pages/api/comments.js, cujo objetivo é devolver os comentários de um post de blog como JSON.

Então, suponhamos que você tenha uma lista de comentários armazenados em um arquivo comments.json:

[
  {
    "comment": "First"
  },
  {
    "comment": "Nice post"
  }
]

Aqui está uma amostra de código, que retorna ao client a lista de comentários:

import comments from './comments.json'

export default (req, res) => {
  res.status(200).json(comments)
}

Ele ouvirá o URL /api/comments para solicitações GET e você pode tentar chamá-lo usando seu navegador:

Screen-Shot-2019-11-07-at-11.14.42

As rotas de API também podem usar roteamento dinâmico, assim como as páginas, usar a sintaxe [] para criar uma rota de API dinâmica, como /pages/api/comments/[id].js, o que recuperará os comentários específicos de um id de determinado post.

Dentro do [id].js, você pode recuperar o valor de id procurando dentro do objeto de req.query:

import comments from '../comments.json'

export default (req, res) => {
  res.status(200).json({ post: req.query.id, comments })
}

Aqui, você pode ver o código acima em ação:

Screen-Shot-2019-11-07-at-11.59.53

Em páginas dinâmicas, você precisaria importar o useRouter do next/router, depois obter o objeto router usando const router = useRouter(). Então, seríamos capazes de obter o valor de id usando router.query.id.

No lado do servidor, tudo é mais fácil, pois a consulta é anexada ao objeto de solicitação.

Se você fizer uma solicitação de POST, tudo funciona do mesmo modo – tudo passa por essa exportação padrão.

Para separar o POST do GET e de outros métodos HTTP (PUT, DELETE), procure o valor do método req.method:

export default (req, res) => {
  switch (req.method) {
    case 'GET':
      //...
      break
    case 'POST':
      //...
      break
    default:
      res.status(405).end() //Method Not Allowed
      break
  }
}

Além de req.query e do método req.method que já vimos, temos acesso aos cookies ao fazermos referência a req.cookies, e ao corpo da solicitação em req.body.

Internamente, tudo isso é desenvolvido pela Micro, uma biblioteca que fornece microsserviços HTTP assíncronos, feita pela mesma equipe que construiu o Next.js.

Você pode fazer uso de qualquer middleware da Micro em nossas rotas de API para adicionar mais funcionalidade.

Executando o código somente no lado do servidor ou do client

Em seus componentes de página, você pode executar o código somente no lado do servidor ou no lado do client, verificando a propriedade window.

Essa propriedade só existe dentro do navegador, portanto você pode verificar

if (typeof window === 'undefined') {

}

Você também pode adicionar o código do lado do servidor nesse bloco.

Do mesmo modo, você pode executar o código do lado do client apenas verificando‌

if (typeof window !== 'undefined') {

}

Dica de JS: usamos aqui o operador typeof, pois não podemos detectar se um valor é undefined de outras maneiras. Não podemos fazer se if (window === undefined), pois obteríamos um erro de tempo de execução "window is not defined" (window não está definido).

O Next.js, como uma otimização para o momento da build, também remove o código que usa essas verificações dos pacotes. Um pacote do lado do client não incluirá o conteúdo envolto em um bloco(typeof window === 'undefined') {}.

Fazendo o deploy da versão em produção

Fazer o deploy de uma aplicação é sempre deixado por último nos tutoriais.

Aqui, quero apresentá-lo mais cedo, só porque é tão fácil fazer um deploy de um aplicação do Next.js que podemos entrar nesse assunto agora e, depois, passar para os outros tópicos mais complexos.

Lembre-se de que, no capítulo "Como instalar o Next.js", eu disse para adicionar essas três linhas à seção de script do package.json:

"scripts": {
  "dev": "next",
  "build": "next build",
  "start": "next start"
}

Usamos npm run dev até o momento, para chamar o comando next instalado localmente em node_modules/next/dist/bin/next. Isso iniciou o servidor de desenvolvimento, que nos forneceu mapas de fonte e recarregamento rápido de código (hot code reloading), duas características muito úteis durante a depuração.

O mesmo comando pode ser invocado para construir o site passando a flag de build, executando npm run build. Então, o mesmo comando pode ser usado para iniciar a aplicação de produção, passando a flag start, executando npm run start.

Esses dois comandos são os que devemos invocar para fazer o deploy com sucesso da versão de produção de nosso site localmente. A versão de produção é altamente otimizada e não vem com mapas de fonte e outras coisas como recarregamento rápido de código, que não seriam benéficas para os nossos usuários finais.

Então, vamos criar um deploy de produção de nossa aplicação. Fazemos o build usando:

npm run build
Screen-Shot-2019-11-06-at-13.46.31

A saída do comando nos diz que algumas rotas (/ e /blog) agora estão pré-renderizadas como HTML estático, enquanto /blog/[id] será servida pelo back-end do Node.js.

Então, você pode executar npm run start para iniciar o servidor de produção localmente:

npm run start
Screen-Shot-2019-11-06-at-13.47.01

Ao visitar http://localhost:3000, veremos a versão de produção da aplicação, localmente.

Fazendo o deploy no Now

No capítulo anterior, fizemos o deploy da aplicação do Next.js localmente.

Como fazemos o deploy em um servidor da web de verdade para que outras pessoas possam acessá-lo?

Uma das maneiras mais simples de se fazer um deploy de uma aplicação do Next é através da plataforma Now, criada pela Zeit, a mesma empresa que criou o projeto de código aberto do Next.js. Você pode usar o Now para fazer o deploy de aplicações do Node.js, sites estáticos e muito mais.

O Now torna a etapa de deploy e a etapa de distribuição de uma aplicação muito simples e rápida. Além das aplicações do Node.js, também há suporte ao deploy de código em Go, PHP, Python e outras linguagens.

Você pode pensar nisso como a "nuvem", pois você não sabe realmente onde será feito o deploy de sua aplicação, mas sabe que terá um URL onde poderá acessá-la.

Now é grátis para começar a usar, com um generoso plano gratuito que atualmente inclui 100GB de hospedagem, mil invocações de funções serverless (texto em inglês) por dia, mil builds por mês, 100GB de largura de banda por mês, e uma localização de CDN (texto em inglês). A página de preços ajuda a ter uma ideia dos custos, se você precisar de mais.

A melhor maneira de começar a usar o Now é por meio da CLI oficial do Now:

npm install -g now

Uma vez que o comando esteja disponível, execute

now login

A aplicação, então, pedirá seu e-mail.

Se você ainda não se registrou, crie uma conta em https://zeit.co/signup antes de continuar, então adicione seu e-mail à CLI do client.

Uma vez feito isso, a partir da pasta raiz do projeto Next.js, execute

now

A aplicação fará o deploy instantaneamente na nuvem do Now, e você receberá o URL único da aplicação:

Screen-Shot-2019-11-06-at-14.21.09

Uma vez executado o programa now, a aplicação é implantada em um URL aleatório sob o domínio now.sh.

Podemos ver três URLs diferentes na saída dada na imagem:

Por que tantos?

O primeiro é o URL que identifica o deploy. Toda vez que fazemos o deploy da aplicação, esse URL mudará.

Você pode testar imediatamente, alterando algo no código do projeto, e executar now novamente:

Screen-Shot-2019-11-06-at-15.08.11

Os outros dois URLs não mudarão. O primeiro é aleatório, enquanto o segundo é o nome do seu projeto (que por padrão é o nome da pasta do projeto atual, o nome de sua conta e, em seguida, now.sh.

Se você visitar o URL, verá a aplicação depois de feito o deploy para a produção.

Screen-Shot-2019-11-06-at-14.21.43

Você pode configurar o Now para que sirva o site para o seu próprio domínio ou subdomínio personalizado, mas eu não vou me aprofundar nisso agora.

O subdomínio now.sh é suficiente para os nossos propósitos de teste.

Analisando os pacotes da aplicação

O Next nos fornece uma forma de analisar os pacotes de códigos que são gerados.

Abra o arquivo package.json da aplicação e, na seção de scripts, adicione estes três novos comandos:

"analyze": "cross-env ANALYZE=true next build",
"analyze:server": "cross-env BUNDLE_ANALYZE=server next build",
"analyze:browser": "cross-env BUNDLE_ANALYZE=browser next build"

Assim:

{
  "name": "primeiroprojeto",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "analyze": "cross-env ANALYZE=true next build",
    "analyze:server": "cross-env BUNDLE_ANALYZE=server next build",
    "analyze:browser": "cross-env BUNDLE_ANALYZE=browser next build"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "next": "^9.1.2",
    "react": "^16.11.0",
    "react-dom": "^16.11.0"
  }
}

Em seguida, instale estes dois pacotes:

npm install --dev cross-env @next/bundle-analyzer

Crie um arquivo next.config.js na raiz do projeto, com este conteúdo:

const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true'
})

module.exports = withBundleAnalyzer({})

Agora execute o comando

npm run analyze
Screen-Shot-2019-11-06-at-16.12.40

Isso deve abrir duas páginas no navegador. Uma para os pacotes dos clients, e outra para os pacotes de servidores:

Screen-Shot-2019-11-06-at-16.11.14
Screen-Shot-2019-11-06-at-16.11.23

Isso é incrivelmente útil. Você pode inspecionar o que ocupa mais espaço nos pacotes, e também usar a barra lateral para excluir os pacotes para ter uma visualização mais fácil dos menores:

Screen-Shot-2019-11-06-at-16.14.12

Módulos de lazy loading

Ser capaz de analisar visualmente um pacote é ótimo, pois podemos otimizar a nossa aplicação muito facilmente.

Digamos que precisamos carregar a biblioteca do Moment em nossos posts no blog. Execute:

npm install moment

Desse modo, a incluiremos no projeto.

Agora, vamos simular o fato de que precisamos dele em duas rotas diferentes: /blog e /blog/[id].

Nós importamos o Moment em pages/blog/[id].js:

import moment from 'moment'

...

const Post = props => {
  return (
    <div>
      <h1>{props.post.title}</h1>
      <p>Published on {moment().format('dddd D MMMM YYYY')}</p>
      <p>{props.post.content}</p>
    </div>
  )
}

Estou apenas adicionando a data de hoje, como um exemplo.

Isto incluirá o Moment.js no pacote de páginas de postagem do blog, como você pode ver executando npm run analyze:

Screen-Shot-2019-11-06-at-17.56.14

Veja que, agora, temos uma entrada vermelha em /blog/[id], a rota em que adicionamos o Moment.js!

Ela passou de cerca de 1kB para 350kB – isso é muito importante. Isso ocorre porque a biblioteca do Moment.js em si é de 349kB.

A visualização dos pacotes dos clients agora nos mostra que o pacote maior é a página um, que antes era muito pequena. 99% do tamanho de seu código é relacionado ao Moment.js.

Screen-Shot-2019-11-06-at-17.55.50

Toda vez que carregarmos um post no blog, teremos todo esse código transferido para o client. O que não é o ideal.

Uma correção seria procurar uma biblioteca com um tamanho menor, pois o Moment.js não é conhecido por ser leve (especialmente de início, com todos os idiomas incluídos). Vamos supor, em função do exemplo, contudo, que devemos usá-lo.

O que podemos fazer é colocar todo o código do Moment em um pacote separado.

Como? Em vez de importar o Moment no nível do componente, realizamos uma importação async dentro do getInitialProps e calculamos o valor a ser enviado ao componente. Lembre-se de que não podemos retornar objetos complexos dentro do objeto retornado pelo getInitialProps(). Então, calculamos a data dentro dele:

import posts from '../../posts.json'

const Post = props => {
  return (
    <div>
      <h1>{props.post.title}</h1>
      <p>Published on {props.date}</p>
      <p>{props.post.content}</p>
    </div>
  )
}

Post.getInitialProps = async ({ query }) => {
  const moment = (await import('moment')).default()
  return {
    date: moment.format('dddd D MMMM YYYY'),
    post: posts[query.id]
  }
}

export default Post

Você vê aquela chamada especial para .default() depois de await import? É necessário referenciar a exportação padrão em uma importação dinâmica (ver https://v8.dev/features/dynamic-import – texto em inglês)

Agora, se executarmos npm run analyze novamente, podemos ver o seguinte:

Screen-Shot-2019-11-06-at-18.00.22

O nosso pacote /blog/[id] é novamente muito pequeno, pois o Moment foi movido para o seu próprio arquivo de pacote, carregado separadamente pelo navegador.

Para onde ir a partir daqui

Há muito mais a saber sobre o Next.js. Eu não falei sobre gerenciar sessões de usuários com login, serverless, gerenciamento de bancos de dados e assim por diante.

O objetivo deste manual não é ensinar tudo. Ao invés disso, ele busca apresentar, gradualmente, todo o poder do Next.js.

O próximo passo que recomendo é fazer uma boa leitura na documentação oficial do Next.js (em inglês) para saber mais sobre todas as características e funcionalidades das quais não falei, além de dar uma olhada em todas as funcionalidades adicionais introduzidas pelos plug-ins do Next.js, algumas das quais são bastante surpreendentes.

Você pode entrar em contato com o autor pelo Twitter.

Confira também o site do autor, flaviocopes.com.

Observação: você pode baixar uma versão em PDF/ePub/Mobi deste tutorial para ler off-line!