00:00:00(música animada) - Olá a todos.
00:00:06Meu nome é Aurora.
00:00:07Sou desenvolvedora web da Noruega.
00:00:09Trabalho como consultora na Crane Consulting e estou ativamente desenvolvendo com o Next.js app router no meu projeto de consultoria atual.
00:00:16Hoje,
00:00:17vou ensinar padrões de composição,
00:00:19cache e arquitetura no Next.js moderno que ajudarão a garantir escalabilidade e desempenho.
00:00:24Primeiro,
00:00:24vamos relembrar o conceito mais fundamental para esta palestra: renderização estática e dinâmica.
00:00:30Encontramos ambos no Next.js app router.
00:00:33A renderização estática nos permite criar sites mais rápidos porque o conteúdo pré-renderizado pode ser armazenado em cache e distribuído globalmente,
00:00:39garantindo que os usuários o acessem mais rapidamente.
00:00:42Por exemplo, o site da Next.js Conf.
00:00:46A renderização estática reduz a carga do servidor porque o conteúdo não precisa ser gerado para cada solicitação do usuário.
00:00:51O conteúdo pré-renderizado também é mais fácil para os rastreadores de mecanismos de busca indexarem,
00:00:56pois já está disponível no carregamento da página.
00:00:58A renderização dinâmica,
00:00:59por outro lado,
00:01:00permite que nossa aplicação exiba dados em tempo real ou frequentemente atualizados.
00:01:05Também nos permite servir conteúdo personalizado,
00:01:07como painéis e perfis de usuário.
00:01:09Por exemplo, o painel da Vercel.
00:01:12Com a renderização dinâmica,
00:01:13podemos acessar informações que só podem ser conhecidas no momento da solicitação.
00:01:16Neste caso,
00:01:17qual usuário está acessando seu painel,
00:01:19que sou eu.
00:01:20Existem certas APIs que podem fazer com que uma página seja renderizada dinamicamente.
00:01:25O uso das props params e search params passadas para as páginas ou seus hooks equivalentes causará a renderização dinâmica.
00:01:32No entanto,
00:01:32com params,
00:01:33podemos predefinir um conjunto de páginas pré-renderizadas usando static params genéricos,
00:01:36e também podemos armazenar em cache as páginas à medida que são geradas pelos usuários.
00:01:40Além disso,
00:01:41a leitura de cookies e cabeçalhos de requisição de entrada fará com que a página opte pela renderização dinâmica.
00:01:46Ao contrário de params,
00:01:47tentar armazenar em cache ou pré-renderizar algo usando cabeçalhos ou cookies causará erros durante a compilação,
00:01:52porque essa informação não pode ser conhecida antecipadamente.
00:01:56Por fim,
00:01:56usar fetch com uma configuração de cache de dados no store também forçará a renderização dinâmica.
00:02:00Então,
00:02:01estas são algumas,
00:02:02há mais algumas APIs que podem causar renderização dinâmica,
00:02:04mas estas são as que mais comumente encontramos.
00:02:06Em versões anteriores do Next,
00:02:08uma página seria renderizada como totalmente estática ou totalmente dinâmica.
00:02:13Uma única API dinâmica em uma página fará com que a página inteira opte pela renderização dinâmica.
00:02:17Por exemplo,
00:02:18fazer uma simples verificação de autenticação para o valor de um cookie.
00:02:20Ao utilizar componentes de servidor React com Suspense,
00:02:23podemos transmitir conteúdo dinâmico,
00:02:24como um banner de boas-vindas personalizado ou recomendações,
00:02:27à medida que ficam prontos,
00:02:28e fornecer apenas fallbacks com Suspense enquanto exibimos conteúdo estático,
00:02:32como um boletim informativo.
00:02:34No entanto,
00:02:34ao adicionar vários componentes assíncronos em uma página dinâmica,
00:02:38como um produto em destaque,
00:02:39eles também seriam executados no momento da solicitação,
00:02:42mesmo que não dependessem de APIs dinâmicas.
00:02:45Então,
00:02:45para evitar o bloqueio do carregamento inicial da página,
00:02:48suspenderíamos e transmitiríamos esses componentes também,
00:02:51fazendo trabalho extra,
00:02:52criando esqueletos e nos preocupando com coisas como o deslocamento cumulativo de layout.
00:02:56No entanto,
00:02:57as páginas são frequentemente uma mistura de conteúdo estático e dinâmico.
00:03:01Por exemplo,
00:03:02um aplicativo de e-commerce que depende de informações do usuário,
00:03:05mas ainda contém principalmente dados estáticos.
00:03:07Ser forçado a escolher entre eles,
00:03:09entre estático ou dinâmico,
00:03:11causa muito processamento redundante no servidor para conteúdo que nunca ou muito raramente muda e não é ideal para o desempenho.
00:03:19Então,
00:03:20para resolver este problema,
00:03:21na Next.js Conf do ano passado,
00:03:23a diretiva use cache foi anunciada.
00:03:26E este ano,
00:03:26como vimos na keynote,
00:03:28está disponível no Next.js 16.
00:03:30Então,
00:03:31com use cache,
00:03:31as páginas não serão mais forçadas à renderização estática ou dinâmica.
00:03:36Elas podem ser ambas.
00:03:37E o Next.js não precisa mais adivinhar o que é uma página com base em se ela acessa coisas como params.
00:03:43Tudo é dinâmico por padrão e use cache nos permite optar explicitamente pelo cache.
00:03:47Use cache permite o cache composable.
00:03:51Podemos marcar uma página,
00:03:52um componente React ou uma função como armazenável em cache.
00:03:55Aqui,
00:03:56podemos realmente armazenar em cache o componente de produtos em destaque porque ele não precisa da requisição e processamento e não usa APIs dinâmicas.
00:04:03E esses segmentos em cache podem ser pré-renderizados e incluídos como parte do shell estático com pré-renderização parcial,
00:04:09o que significa que os produtos em destaque agora estão disponíveis no carregamento da página e não precisam ser transmitidos.
00:04:14Então,
00:04:15agora que temos esse conhecimento de base importante,
00:04:18vamos fazer uma demonstração.
00:04:19Uma melhoria de uma base de código com problemas comuns frequentemente encontrados em aplicativos Next.js.
00:04:24Isso inclui prop drilling profundo,
00:04:25dificultando a manutenção e refatoração de recursos,
00:04:28JavaScript redundante no lado do cliente e componentes grandes com múltiplas responsabilidades,
00:04:32e falta de renderização estática,
00:04:33levando a custos adicionais de servidor e desempenho degradado.
00:04:36Então, sim, vamos começar.
00:04:37E me dê um segundo aqui.
00:04:50Certo, ótimo.
00:04:54Então, este é um aplicativo muito simples.
00:04:56É inspirado em uma plataforma de e-commerce.
00:04:59E deixe-me fazer uma demonstração inicial aqui.
00:05:01Então, posso carregar esta página.
00:05:03Tenho algum conteúdo como este produto em destaque.
00:05:06Tenho categorias em destaque, diferentes dados de produtos.
00:05:09Há também esta página "Navegar em Tudo" aqui,
00:05:13onde posso ver todos os produtos da plataforma e navegar entre eles.
00:05:20Depois,
00:05:20temos esta página "Sobre" aqui,
00:05:22que é apenas estática.
00:05:24Também posso fazer login como usuário.
00:05:27E isso me conectará ao meu usuário.
00:05:30E também obter conteúdo personalizado no meu painel aqui.
00:05:33Como,
00:05:34por exemplo,
00:05:34produtos recomendados ou estes descontos personalizados aqui.
00:05:38Então, percebam que há uma boa mistura.
00:05:42Ah, também mais uma página que esqueci de mostrar.
00:05:45A página do produto, a mais importante.
00:05:47Também aqui,
00:05:48podemos ver informações do produto e depois salvá-lo se quisermos para o nosso usuário.
00:05:52Então,
00:05:52percebam que há uma boa mistura de conteúdo estático e dinâmico neste aplicativo por causa de todos os nossos recursos dependentes do usuário.
00:05:59Vamos também dar uma olhada no código, que estaria aqui.
00:06:05Então, estou usando o app router aqui, claro, no Next.js 16.
00:06:08Tenho todas as minhas diferentes páginas,
00:06:10como a página "Sobre",
00:06:11a página "Tudo",
00:06:12nossa página de produto.
00:06:13Também tenho...
00:06:14estou usando feature slicing aqui para manter minha pasta app limpa..
00:06:17Tenho diferentes componentes e consultas conversando com meu banco de dados com Prisma.
00:06:23Então, sim, e eu propositalmente desacelerei tudo isso.
00:06:25É por isso que temos esta etapa de carregamento realmente longa,
00:06:28apenas para que possamos ver mais facilmente o que está acontecendo.
00:06:31Então,
00:06:31os problemas comuns que queríamos resolver aqui e que realmente temos nesta aplicação eram o prop drilling,
00:06:37dificultando a manutenção e refatoração de recursos,
00:06:39excesso de JavaScript no lado do cliente e falta de renderização estática,
00:06:43levando a custos adicionais de servidor e desempenho integrado.
00:06:47Então,
00:06:47o objetivo desta demonstração é basicamente apenas melhorar este aplicativo com alguns padrões inteligentes de composição,
00:06:54cache e arquitetura para corrigir esses recursos comuns e torná-lo mais rápido,
00:06:58mais escalável e mais fácil de manter.
00:07:01Então, vamos começar com isso.
00:07:02O primeiro problema que queremos corrigir está realmente relacionado ao prop drilling.
00:07:05E isso seria aqui na página.
00:07:10Percebam que aqui, tenho esta variável loggedIn no topo.
00:07:15E vocês podem ver que estou passando-a para alguns componentes.
00:07:17Na verdade,
00:07:17ela foi passada por vários níveis até este banner pessoal.
00:07:20Então,
00:07:20isso vai dificultar a reutilização de coisas aqui porque sempre temos essa dependência de loggedIn para o nosso banner de boas-vindas.
00:07:28Então,
00:07:28com os componentes de servidor,
00:07:30a melhor prática seria realmente empurrar a busca de dados para os componentes que estão usando isso e resolver as promessas mais profundamente na árvore.
00:07:37E para que isso seja autenticado,
00:07:39desde que esteja usando fetch ou algo como React cache,
00:07:42podemos duplicar várias chamadas disso e podemos simplesmente reutilizá-lo onde quisermos dentro de nossos componentes.
00:07:48Então, seria totalmente bom reutilizar.
00:07:51Então,
00:07:51agora podemos realmente mover isso para a seção personalizada aqui.
00:07:54E não vamos mais precisar desta prop.
00:07:57E apenas colocá-lo diretamente — opa — aqui.
00:08:01E não vamos mais precisar passar isso.
00:08:04E como estamos agora movendo esta chamada assíncrona para a seção personalizada,
00:08:07não estamos mais bloqueando a página.
00:08:09Podemos ir em frente e suspender isso com um simples Suspense aqui.
00:08:13E não vamos precisar deste fallback.
00:08:16Quanto ao banner de boas-vindas,
00:08:19suponho que faremos o mesmo.
00:08:22Mas tentar usar — obter a variável ou valor loggedIn aqui,
00:08:26não funciona,
00:08:27certo?
00:08:27Porque este é um componente cliente.
00:08:29Então, precisamos resolver isso de uma maneira diferente.
00:08:30E vamos usar um padrão bem inteligente aqui para resolver isso.
00:08:33Na verdade,
00:08:34vamos para o layout e envolver tudo aqui com um Auth Provider.
00:08:39Então,
00:08:39vou apenas colocar isso em torno de todo o meu aplicativo aqui e obter esta variável loggedIn aqui.
00:08:45E eu definitivamente não quero bloquear todo o meu root layout.
00:08:48Vamos em frente e remover o await aqui.
00:08:50E apenas passar isso como uma promessa para este Auth Provider.
00:08:55E isso pode apenas conter essa promessa.
00:08:57Pode ficar lá, esperando, até estarmos prontos para lê-lo.
00:09:01Então, agora temos isso configurado.
00:09:03Isso significa que podemos realmente ir em frente e nos livrar desta prop,
00:09:08em primeiro lugar.
00:09:09E vamos nos livrar desta que está sendo passada para o banner pessoal.
00:09:12E vamos nos livrar do prop drilling também aqui ou da assinatura.
00:09:16E agora podemos usar este Auth Provider para buscar este valor loggedIn localmente dentro do banner pessoal com useAuth com o provedor que acabamos de criar.
00:09:26E lê-lo com use.
00:09:28Então,
00:09:28isso realmente funcionará de uma forma que precisamos suspender isso enquanto está sendo resolvido.
00:09:33Então,
00:09:33agora eu apenas co-localizei aquela pequena busca de dados dentro do banner pessoal.
00:09:37E não preciso passar essas props por aí.
00:09:40E enquanto isso está sendo resolvido,
00:09:42vamos em frente e suspender este também com um fallback.
00:09:44E vamos apenas fazer um banner geral aqui para evitar qualquer deslocamento cumulativo estranho.
00:09:51E finalmente, também nos livramos deste.
00:09:53Então, agora este banner de boas-vindas é composable.
00:09:58É reutilizável.
00:09:59Não temos props ou dependências estranhas na página inicial.
00:10:02E como podemos reutilizar isso tão facilmente,
00:10:05vamos em frente e adicioná-lo também a esta página de navegação aqui,
00:10:10que estará aqui.
00:10:11E posso simplesmente usá-lo aqui sem quaisquer dependências.
00:10:15Então,
00:10:16através desses padrões,
00:10:17somos capazes de manter uma boa arquitetura de componentes utilizando React cache,
00:10:24React use,
00:10:25e tornar nossos componentes mais utilizáveis e composable.
00:10:30Certo.
00:10:31Vamos abordar o próximo desafio comum,
00:10:33que seria o JavaScript excessivo no lado do cliente e componentes grandes com múltiplas responsabilidades.
00:10:40Na verdade, isso também está na página "Tudo" aqui.
00:10:43E novamente,
00:10:43teremos que trabalhar neste banner de boas-vindas.
00:10:46Atualmente, é um componente cliente.
00:10:48E a razão pela qual é um componente cliente é porque tenho este estado de
00:10:51"dispensado"
00:10:52muito simples aqui.
00:10:53Posso simplesmente clicar nisso.
00:10:54É uma boa interação de UI.
00:10:56Tudo bem.
00:10:57O que não é tão bom,
00:10:58porém,
00:10:58é que por causa disso,
00:10:59eu converto todo este componente em um componente do lado do cliente ou um componente cliente.
00:11:04E eu até uso swr para buscar dados no lado do cliente.
00:11:07Agora tenho esta camada de API aqui.
00:11:08Não tenho mais segurança de tipo nos meus dados.
00:11:11Sim, isso não é necessário.
00:11:12E também estamos quebrando a separação de preocupações aqui porque estamos envolvendo lógica de UI com dados.
00:11:18Então,
00:11:18vamos em frente e usar outro padrão inteligente para corrigir isso.
00:11:21É chamado de padrão donut.
00:11:23Basicamente,
00:11:23o que vou fazer é extrair isso para um wrapper do lado do cliente.
00:11:27Então, vamos criar um novo componente aqui.
00:11:29E vamos chamá-lo de BannerContainer.
00:11:32E este vai conter nossa lógica interativa com a diretiva use client.
00:11:37Podemos criar a assinatura.
00:11:38Podemos colar tudo o que tínhamos antes.
00:11:42E em vez de usar esses banners,
00:11:44vou apenas encaixar uma prop aqui,
00:11:47que será children.
00:11:48É por isso que é chamado de padrão donut.
00:11:50Estamos apenas criando esta lógica de UI de wrapper em torno de conteúdo renderizado no servidor,
00:11:54ou poderia ser conteúdo renderizado no servidor.
00:11:56E então,
00:11:57como não temos mais esta dependência do lado do cliente,
00:11:59podemos ir em frente e remover o use client.
00:12:01Podemos usar nossa função assíncrona isAuth aqui em vez disso.
00:12:06Podemos transformar isso em um componente de servidor assíncrono.
00:12:09Podemos até substituir a busca de dados do lado do cliente pela busca de dados do lado do servidor.
00:12:11Então,
00:12:12deixe-me ir em frente e apenas obter os dados de desconto diretamente aqui.
00:12:16Dados de desconto.
00:12:18E apenas utilizar nosso modelo mental regular como antes com segurança de tipo.
00:12:24E isso significa que também posso excluir esta camada de API com a qual não quero trabalhar de qualquer forma.
00:12:29Finalmente,
00:12:29para o isLoading,
00:12:30podemos apenas exportar um novo banner de boas-vindas aqui com nosso BannerContainer do padrão donut contendo conteúdo renderizado no servidor.
00:12:38E isso significa que não precisamos mais deste isLoading.
00:12:40Então,
00:12:41basicamente,
00:12:41refatoramos tudo isso em um componente de servidor e extraímos um ponto de lógica de UI.
00:12:46Mas o que é isso?
00:12:48Parece que tenho outro erro.
00:12:51Então, isso é na verdade por causa do Motion.
00:12:53Usem o Motion.
00:12:54É uma biblioteca de animação realmente ótima,
00:12:57mas requer a diretiva useClient.
00:12:59E novamente,
00:13:00não precisamos usar useClient apenas para animação.
00:13:03Podemos criar,
00:13:04novamente,
00:13:05um wrapper de padrão donut e apenas extrair wrappers para essas animações.
00:13:10E isso significa que não precisamos converter nada aqui para o lado do cliente.
00:13:14E provavelmente estou perdendo algo aqui embaixo.
00:13:17Sim.
00:13:18Aí está.
00:13:21Então, agora tudo aqui foi convertido para o servidor.
00:13:23Temos a mesma interação.
00:13:24Ainda temos nossa lógica interativa aqui,
00:13:27mas agora temos esta única maneira de buscar dados.
00:13:29E temos muito menos JS do lado do cliente.
00:13:31Na verdade,
00:13:32estou usando este padrão donut para este UI boundary helper,
00:13:39que se parece com isso.
00:13:42Estão vendo?
00:13:43Então,
00:13:44isso meio que mostra,
00:13:44novamente,
00:13:45o que quero dizer,
00:13:45certo?
00:13:45Com o padrão donut,
00:13:46temos este componente cliente em torno de um componente servidor.
00:13:49Também marquei muitos dos meus outros componentes com este UI helper aqui.
00:13:53Também aqui, tenho mais componentes de servidor.
00:13:56Vamos em frente e melhorar esses também,
00:13:59já que estamos ficando muito bons nisso.
00:14:01Eles estão no rodapé.
00:14:04Essas categorias...
00:14:05quero dizer,
00:14:05tenho este bom componente buscando seus próprios dados..
00:14:08E eu só queria adicionar este showMore,
00:14:11caso fique muito longo.
00:14:14E com o padrão donut,
00:14:15posso apenas envolver um componente showMore aqui.
00:14:20E isso conterá minha lógica de UI.
00:14:23E parece com isso, certo?
00:14:27Bem legal.
00:14:28E isso agora contém a lógica do cliente,
00:14:31permitindo-nos usar o estado.
00:14:33Estamos usando a contagem de children e para array para fatiar isso.
00:14:36E o que é tão legal aqui é que esses dois são agora componentes totalmente composable e reutilizáveis que funcionam juntos assim.
00:14:42Então,
00:14:42esta é realmente a beleza desses padrões que estamos aprendendo aqui.
00:14:45Você pode usar isso para qualquer coisa.
00:14:50Eu também uso para este modal aqui.
00:14:52Sim,
00:14:52apenas lembrem-se disso da próxima vez que estiverem considerando adicionar qualquer tipo de lógica de cliente aos seus componentes de servidor.
00:14:59OK, conhecemos o padrão donut.
00:15:01Sabemos como utilizá-lo para criar esses componentes composable e evitar client-side JS,
00:15:07então podemos passar para o problema final.
00:15:10Deixe-me fechar isso novamente.
00:15:13Então,
00:15:14isso seria com a falta de estratégias de renderização estática,
00:15:18certo?
00:15:18Olhando para a saída da minha compilação,
00:15:20na verdade,
00:15:21tenho cada página como uma página dinâmica aqui.
00:15:24Então,
00:15:24isso significa que sempre que eu carregar algo aqui,
00:15:27isso será executado para cada usuário.
00:15:29Desculpe.
00:15:30Cada usuário que abrir isso vai ter este estado de carregamento.
00:15:33Isso vai desperdiçar custos de servidor,
00:15:35piorando o desempenho.
00:15:37E isso significa também que algo dentro das minhas páginas está causando renderização dinâmica ou forçando a renderização dinâmica para todas as minhas páginas.
00:15:45Na verdade, está dentro do meu root layout.
00:15:49Não sei se vocês já experimentaram isso.
00:15:51Está aqui.
00:15:53No meu cabeçalho, tenho este perfil de usuário.
00:15:57E isso,
00:15:57claro,
00:15:58está usando cookies para obter o usuário atual,
00:16:00e isso significa que todo o resto também é renderizado dinamicamente.
00:16:02Porque,
00:16:03novamente,
00:16:03as páginas podem ser dinâmicas ou estáticas,
00:16:06certo?
00:16:06Este é um problema bastante comum e algo que já foi resolvido em versões anteriores do Next,
00:16:11então vamos ver o que podemos fazer.
00:16:13Uma coisa que poderíamos fazer é criar um grupo de rotas e dividir nosso aplicativo em seções estáticas e dinâmicas,
00:16:20o que me permitiria extrair minha página "Sobre".
00:16:23Eu poderia renderizar isso estaticamente.
00:16:25Tudo bem para alguns aplicativos,
00:16:27mas no meu caso,
00:16:28a página importante é a página do produto,
00:16:30e esta ainda é dinâmica,
00:16:31então não é muito útil.
00:16:33Que tal esta estratégia?
00:16:35Então,
00:16:35aqui estou criando este request context param codificando um certo estado na minha URL,
00:16:40e então posso usar generate static params para gerar todas as diferentes variantes das minhas páginas.
00:16:46Isso,
00:16:47na verdade,
00:16:47combinado com a busca de dados do usuário no lado do cliente,
00:16:51me permitiria ter isso em cache na minha página de produto.
00:16:54Definitivamente um padrão viável.
00:16:55É recomendado pelo SDK Vercel Flags,
00:16:58chamado de padrão precompute,
00:17:00eu acho.
00:17:00Mas isso é realmente complexo,
00:17:02e tenho várias maneiras de buscar dados.
00:17:04E,
00:17:04na verdade,
00:17:05não quero reescrever todo o meu aplicativo para isso.
00:17:07E se não tivéssemos que fazer nenhuma dessas soluções alternativas?
00:17:10E se houvesse uma maneira mais simples?
00:17:12Bem, existe.
00:17:14Vamos voltar ao nosso aplicativo novamente.
00:17:17Então,
00:17:17podemos realmente ir para o next config e apenas habilitar cache components.
00:17:23Ah, legal.
00:17:25OK,
00:17:25e o que isso faz,
00:17:26como vocês sabem da keynote,
00:17:28é que ele realmente fará com que todas as nossas chamadas assíncronas optem pelo tempo de requisição ou dinâmico.
00:17:34E também nos dará erros sempre que tivermos alguma chamada assíncrona não suspensa,
00:17:39e nos dará esta diretiva use cache que podemos usar para armazenar em cache granularmente uma página,
00:17:46uma função ou um componente.
00:17:48Então, sim, vamos em frente e utilizar isso.
00:17:51Podemos começar com a página inicial aqui.
00:17:55Vamos dar uma olhada.
00:17:56Então,
00:17:56novamente,
00:17:57tenho esta mistura de conteúdo estático e dinâmico.
00:17:59Tenho meu banner de boas-vindas para mim,
00:18:01algo para você também para mim.
00:18:03Vamos dar uma olhada nisso com este UI helper novamente.
00:18:06Então,
00:18:06por exemplo,
00:18:07o banner é renderizado dinamicamente com isso aqui.
00:18:10Enquanto eu marquei isso como renderização híbrida porque o herói está buscando essa coisa assíncrona e está indo bem devagar.
00:18:18Mas não depende de nenhum tipo de dado do usuário ou APIs dinâmicas.
00:18:21Então,
00:18:22isso significa que tudo o que é renderizado de forma híbrida aqui pode ser reutilizado em todas as requisições e em todos os usuários.
00:18:27E podemos usar a diretiva use cache nisso.
00:18:30Então,
00:18:30vamos adicionar a diretiva use cache aqui e marcar isso como armazenado em cache.
00:18:35E isso me permitirá — sempre que eu recarregar esta página — eu não salvei isso.
00:18:43Aí está.
00:18:44Não recarregará esta parte porque está em cache.
00:18:47Agora é estático, certo?
00:18:49E também existem outras APIs relacionadas,
00:18:52como a cache tag para me permitir tipar isso ou validar a entrada de cache específica granularmente ou definir meu período de revalidação.
00:19:01Mas para esta demonstração,
00:19:03vamos focar apenas na diretiva simples.
00:19:05Agora que tenho esta diretiva use cache,
00:19:07posso realmente remover meu limite de Suspense em torno deste herói.
00:19:10E isso significa — bem,
00:19:12o que isso fará é que a pré-renderização parcial pode realmente ir em frente e incluir isso no shell pré-renderizado estaticamente para que este herói,
00:19:20neste caso,
00:19:21faça parte da minha saída de compilação.
00:19:23Vamos fazer a mesma coisa para todo o resto nesta página que pode ser compartilhado.
00:19:28Por exemplo, tenho estas categorias em destaque aqui.
00:19:31Vamos em frente e fazer o mesmo lá.
00:19:33E adicionar a diretiva use cache e marcar isso como armazenado em cache.
00:19:37Assim.
00:19:39E podemos remover o limite de Suspense.
00:19:40Não vamos mais precisar disso.
00:19:43O mesmo para os produtos em destaque.
00:19:44Vamos adicionar use cache e marcar isso como armazenado em cache.
00:19:48Opa.
00:19:50E então remover o limite de Suspense.
00:19:52Então, percebam quanta complexidade eu consigo remover aqui.
00:19:55Não preciso me preocupar com meus esqueletos,
00:19:57meu deslocamento cumulativo de layout que eu estava fazendo antes.
00:20:00E a página não é mais — ou não temos mais esta limitação de nível de página estático versus dinâmico.
00:20:07Então,
00:20:08agora,
00:20:08quando eu carregar esta página,
00:20:10vocês verão que tudo aqui está em cache,
00:20:12exceto por este conteúdo verdadeiramente específico do usuário.
00:20:16Certo.
00:20:18Então, isso é bem legal.
00:20:19Vamos para a página "Navegar" e fazer a mesma coisa lá.
00:20:24Sim.
00:20:25Já marquei todos os meus limites aqui para que vocês possam entender facilmente o que está acontecendo.
00:20:29E quero pelo menos armazenar em cache estas categorias.
00:20:33Parece que estou recebendo um erro, porém.
00:20:37Talvez vocês reconheçam isso.
00:20:38Então, significa que tenho uma rota de bloqueio.
00:20:40E não estou usando um limite de Suspense quando deveria.
00:20:43Atualizando isso, é verdade, hein?
00:20:46Isso é realmente lento.
00:20:47E está causando problemas de desempenho e má UX.
00:20:50Então, isso é ótimo.
00:20:51Use cache ou cache components está me ajudando a identificar minhas rotas de bloqueio.
00:20:55Vamos realmente ver o que está acontecendo lá dentro.
00:20:57Então, este é o problema, certo?
00:20:59Estou buscando essas categorias no nível superior e não tenho nenhum limite de Suspense acima delas.
00:21:03Basicamente, precisamos fazer uma escolha.
00:21:05Ou adicionamos um limite de Suspense acima ou optamos pelo cache.
00:21:09Vamos fazer a coisa simples primeiro e apenas adicionar um loading.tsx aqui.
00:21:12E vamos adicionar uma página de carregamento aqui,
00:21:18uma bela UI de esqueleto.
00:21:21Isso é muito bom.
00:21:21Resolveu o erro,
00:21:22mas não tenho nada útil acontecendo nesta página enquanto espero.
00:21:25Não consigo nem pesquisar.
00:21:27Então,
00:21:28com cache components,
00:21:29dinâmico é como — ou estático versus dinâmico é como uma escala.
00:21:33E cabe a nós decidir o quanto de estático queremos em nossas páginas.
00:21:37Então,
00:21:37vamos mover esta página mais para o estático e apenas deletar este loading.tsx novamente.
00:21:43E então utilizar os padrões que estávamos aprendendo anteriormente para empurrar esta busca de dados para o componente e co-localizá-la com a UI.
00:21:50Então,
00:21:51mova isso para meus filtros de categoria responsivos aqui.
00:21:54Tenho dois por causa do design responsivo.
00:21:57Posso realmente ir em frente e apenas adicioná-lo aqui.
00:22:01Opa.
00:22:03E importar isso.
00:22:05Não preciso mais deste prompt.
00:22:06Na verdade, meu componente está se tornando mais composable.
00:22:09E em vez de suspendê-lo,
00:22:11vamos apenas adicionar a diretiva use cache.
00:22:14E isso deve ser suficiente.
00:22:16Então,
00:22:17percebam como estou sendo forçado a pensar mais sobre onde estou resolvendo minhas promessas e realmente melhorando minha arquitetura de componentes através disso.
00:22:24Não preciso suspender isso.
00:22:25Isso será apenas incluído no shell estático aqui.
00:22:28A lista de produtos, deixe-me apenas mantê-la atualizada.
00:22:35Assim, posso recarregar isso toda vez.
00:22:37Enquanto as categorias na parte inferior,
00:22:39também quero armazenar em cache.
00:22:41Então, vamos em frente e ir para o rodapé.
00:22:44E como estou usando o padrão donut aqui,
00:22:47isso pode realmente ser armazenado em cache,
00:22:50mesmo que esteja dentro desta parte da UI que é interativa.
00:22:54Então, isso está totalmente bem.
00:22:55Então,
00:22:56esse padrão não foi bom apenas para composição,
00:22:58mas também para cache.
00:22:58Acho que tenho mais um erro lá.
00:23:03Vamos ver o que é.
00:23:04Ainda tenho este erro.
00:23:08Isso é na verdade por causa desses search frames.
00:23:10Search frames, como sabemos, é uma API dinâmica.
00:23:12Não consigo armazenar isso em cache.
00:23:13Mas posso resolvê-lo mais profundamente para revelar mais da minha UI e torná-la estática.
00:23:18Então,
00:23:18vamos em frente e mover isso para baixo,
00:23:21passá-lo como uma promessa para a lista de produtos.
00:23:24Vamos tipar isso como uma promessa aqui, assim.
00:23:30Vamos resolvê-lo dentro da lista de produtos,
00:23:33usar os parâmetros de busca resolvidos aqui e aqui.
00:23:36E como isso está suspenso aqui, o erro desaparecerá.
00:23:40Então,
00:23:41recarregando isso,
00:23:42a única coisa que está recarregando aqui é apenas a parte que escolhi especificamente para ser dinâmica.
00:23:48Todo o resto pode ser armazenado em cache.
00:23:49E isso significa que posso interagir com meu banner ou até mesmo pesquisar porque essa parte já foi pré-renderizada.
00:23:57Certo,
00:23:57vamos fazer a página final aqui,
00:24:00que é a página do produto,
00:24:02que é a mais difícil e a mais importante.
00:24:05Está muito ruim agora.
00:24:08Isso é super importante para uma plataforma de e-commerce,
00:24:10aparentemente.
00:24:11Certo, vamos em frente e consertar esse também.
00:24:15Então, aqui tenho esta página de produto.
00:24:18Vamos começar a armazenar em cache apenas o conteúdo reutilizável aqui,
00:24:22por exemplo,
00:24:22o próprio produto.
00:24:23E apenas adicionar use cache aqui e marcar isso como armazenado em cache.
00:24:27Isso deve estar bem.
00:24:28Isso significa que podemos remover o limite de Suspense aqui.
00:24:33Certo,
00:24:33e isso não está mais recarregando a cada requisição aqui,
00:24:37certo?
00:24:38Para os detalhes do produto, vamos fazer a mesma coisa.
00:24:40Vamos adicionar use cache.
00:24:41Vamos marcá-lo como armazenado em cache e ver se isso também funcionará.
00:24:47Não funcionou.
00:24:48Na verdade, este é um erro diferente.
00:24:50Está me dizendo que estou tentando usar APIs dinâmicas dentro deste segmento em cache.
00:24:54E isso é verdade.
00:24:54Estou usando o botão "Salvar Produto", certo?
00:24:56Isso me permitiu clicar e alternar o estado salvo.
00:25:00Então, o que vocês acham que podemos fazer com isso?
00:25:03Podemos usar o padrão donut novamente.
00:25:06Na verdade,
00:25:06também podemos encaixar segmentos dinâmicos em segmentos de cache.
00:25:10Então, estamos intercalando-os como antes, mas com cache.
00:25:12Então, isso é bem legal.
00:25:14Vamos em frente e adicionar os children aqui assim.
00:25:19E isso removerá o erro.
00:25:21E posso apenas envolver isso em torno deste único segmento dinâmico da minha página aqui,
00:25:27remover o limite de Suspense e adicionar apenas uma pequena UI de marcador para aquela parte dinâmica da página.
00:25:34E vamos ver como isso fica agora.
00:25:40Então,
00:25:40percebam como quase toda a UI está disponível,
00:25:43mas tenho este pequeno pedaço que é dinâmico,
00:25:46e tudo bem.
00:25:47Todo o resto ainda está lá.
00:25:48E vamos deixar as avaliações dinâmicas porque poderíamos mantê-las atualizadas.
00:25:53Ainda há mais um erro.
00:25:54Vamos apenas resolver isso rapidamente.
00:25:56Novamente, este é o params.
00:25:58Estou recebendo ajuda de que preciso fazer uma escolha: ou adicionar um loading fallback ou armazenar isso em cache.
00:26:04Vamos apenas usar generate static params neste caso.
00:26:07Depende do seu caso de uso e do seu conjunto de dados.
00:26:10Mas,
00:26:10para este caso,
00:26:11vou apenas adicionar algumas páginas pré-renderizadas e predefinidas e depois apenas armazenar em cache o resto à medida que são geradas pelos usuários.
00:26:17E isso removerá meu erro aqui.
00:26:20Então, acho que terminei minha refatoração.
00:26:22Vamos em frente e dar uma olhada na versão implantada e ver como ela se parece.
00:26:26Então, acabei de implantar isso no Vercel.
00:26:27E lembrem-se,
00:26:29eu propositalmente desacelerei muitas buscas de dados aqui.
00:26:35E ainda assim,
00:26:36quando eu carrego esta página inicialmente,
00:26:39tudo já está disponível.
00:26:40A única coisa aqui são apenas aqueles poucos segmentos dinâmicos,
00:26:44como os descontos e o "para você".
00:26:46O mesmo com o "Navegar em Tudo".
00:26:47Toda a UI já está disponível.
00:26:50E para o produto em si, parece instantâneo.
00:26:54E lembrem-se,
00:26:55novamente,
00:26:55que todos esses segmentos em cache serão incluídos com o shell estático com pré-renderização parcial.
00:27:00E pode ser pré-buscado usando o prefetching aprimorado no novo roteador cliente do Next 16.
00:27:05Então, isso significa que cada navegação apenas...
00:27:08parece tão rápido, certo?.
00:27:09Certo,
00:27:10para resumir,
00:27:11com cache components,
00:27:13não há mais estático versus dinâmico.
00:27:17E não precisamos evitar APIs dinâmicas ou comprometer o conteúdo dinâmico.
00:27:28E podemos pular esses hacks e soluções alternativas complexas usando múltiplas estratégias de busca de dados apenas para aquele...
00:27:35este acerto de cache, como eu mostrei a vocês..
00:27:37Então,
00:27:38no Next.js moderno,
00:27:39dinâmico versus estático é uma escala.
00:27:40E nós decidimos o quanto de estático queremos em nossos aplicativos.
00:27:43E desde que sigamos certos padrões,
00:27:45podemos ter um modelo mental que é performático,
00:27:48composable e escalável por padrão.
00:27:50Então, vamos voltar aos slides.
00:27:53Então,
00:27:53se vocês não estão — não estavam já impressionados com a velocidade disso,
00:27:56esta é a pontuação do Lighthouse.
00:27:56Então,
00:27:57coletei alguns dados de campo com o Vercel Speed Insights.
00:28:00Então,
00:28:00temos uma pontuação de 100 em todas as páginas mais importantes,
00:28:03a página inicial,
00:28:04a página do produto e a lista de produtos,
00:28:06mesmo que sejam altamente dinâmicas.
00:28:08Então,
00:28:08vamos finalmente resumir os padrões que garantirão escalabilidade e desempenho em aplicativos Next.js e nos permitirão aproveitar as últimas inovações e obter pontuações como esta.
00:28:18Primeiramente,
00:28:19podemos refinar nossa arquitetura resolvendo promessas profundamente na árvore de componentes e buscando dados localmente dentro dos componentes usando React Cache para evitar trabalho duplicado.
00:28:28Podemos evitar a passagem excessiva de props para componentes cliente usando provedores de contexto combinados com React Use.
00:28:35Em segundo lugar,
00:28:35podemos compor componentes de servidor e cliente usando o padrão donut para reduzir o JavaScript do lado do cliente,
00:28:40manter uma clara separação de preocupações e permitir a reutilização de componentes.
00:28:43E este padrão nos permitirá ainda mais armazenar em cache nossos componentes de servidor compostos posteriormente.
00:28:50E,
00:28:50finalmente,
00:28:51podemos armazenar em cache e pré-renderizar com use cache,
00:28:53seja por página,
00:28:54componente ou função,
00:28:55para eliminar processamento redundante,
00:28:56impulsionar o desempenho e o SEO,
00:28:58e permitir que a pré-renderização parcial renderize estaticamente esses segmentos do aplicativo.
00:29:01E se nosso conteúdo for verdadeiramente dinâmico,
00:29:04podemos suspendê-lo com fallbacks de carregamento apropriados.
00:29:07E lembrem-se que tudo isso está conectado.
00:29:09Então,
00:29:09quanto melhor sua arquitetura,
00:29:10mais fácil será compor,
00:29:11e mais fácil será armazenar em cache e pré-renderizar com os melhores resultados.
00:29:15Por exemplo,
00:29:15resolver APIs dinâmicas profundamente na árvore permitirá que você crie um shell estático parcialmente renderizado maior.
00:29:22E com isso,
00:29:22este é o repositório da versão completa do aplicativo.
00:29:25Há tantas coisas que nem mostrei lá que vocês podem conferir.
00:29:29E vocês podem escanear o código QR para encontrar minhas redes sociais lá,
00:29:32junto com o repositório,
00:29:34se não quiserem tirar uma foto e digitar vocês mesmos.
00:29:36Então, sim, é isso para mim.
00:29:37Obrigado, Next.js Conf, por me receberem aqui.
00:29:39[MÚSICA TOCANDO]