Como utilizamos Partytown com Next.JS para aumentar nossa performance, significamente
Para explicar o problema, primeiro precisamos citar A main thread. No qual, de modo simplificado, é responsável pela renderização da página, executando algumas tarefas, como:
Então, é coerente dizer que, quanto mais demorado é a finalização da execução da Main Thread, mais demorado fica para nosso site terminar de renderizar.
E esse problema só aumenta conforme vamos adicionando mais Javascript na página, pois toda vez que o a main thread está ocupada, sua página web não responde a eventos do usuário e isso impacta diretamente na performance e indexação do seu site.
Então resumidamente, quanto menos Javascript rodarmos na Main Thread enquanto renderizamos nossa página, melhor.
Os frameworks atuais (Next.JS & React.JS) já adicionam relativamente bastante Javascript às páginas e esses sim são essenciais para a renderização dos sites e, utilizando tecnicas como:
Conseguimos atingir uma performance muito boa no Core Web Vitals.
Porém, você provavelmente já se deparou com o cenário em que precisa adicionar algum Script de terceiro no seu frontend, certo? E, dependendo em qual contexto ja trabalhou, pode já ter passado pela situação onde esses Scripts são tantos, que começam a causar mais problemas do que prometem auxiliar.
Um domínio de frontend que esse problema costuma ser comúm, são nos e-commerces e blogs de notícias, por conta de ser uma forma de marketing. Onde, por exemplo, para cada rede social que precise coletar informações da navegação do usuário, o modo mais rápido acaba sendo a adição de um código Javascript.
Script de terceiro do TikTok Pixel. Onde utilizamos o Script do Tiktok para gerar métricas de produtos vistos, por exemplo.
Aqui vou usar o site da Magazineluiza como exemplo, pois foi onde conseguimos (eu e meu time), evoluir de forma significativa o uso dos scripts de terceiros, dado que temos muitas integrações (mais de 10 Scripts).
[Spoiler]:
Em teoria, o Next.JS em suas últimas versões já contempla com a possibilidade de carregar seus scripts de terceiros utilizando o Partytown.JS, Script Optimization - Next.JS.
Porém, a configuração padrão atualmente contém algumas limitações, principalmente para cenários mais complexos de configuração do próprio PartyTown. Exemplo, que irei entrar em mais detalhes a seguir, precisamos escrever a config de resolveUrl de forma customizada.
E atualmente a configuração padrão do Next.JS não permite tal nível de configuração.
Sendo assim, não iremos utilizar a configuração no next.config.js:
.json
experimental: {
nextScriptWorkers: true,
},E sim configurar diretamente no arquivo do _document.jsx:
.jsx
import { Partytown } from '@builder.io/partytown/react';
export default class CustomDocument extends Document {
const partytownHtml = {
__html: `
partytown = {
resolveUrl: (url, location) => {
return url;
}
}
`
}
render() {
return (
<Head>
<script dangerouslySetInnerHTML={partytownHtml} />
<Partytown debug={ false } />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}Reparando que, o primeiro script antes da lib, é para escrevermos as configurações necessárias, que é lida no objeto window.partytown.
Com a configuração que mostramos a cima, já é possível utilizar a biblioteca para o carregamento de seus scripts, apenas adicionando o type "text/partytown". Exemplo:
.jsx
import Script from 'next/script'
export default function Home() {
return (
<>
<Script src="https://example.com/script.js" type="text/partytown" />
</>
)
}Porém, como dito anteriormente, nosso caso no Magalu é mais complexo, pois temos muitos scripts.
E muitos deles ao serem carregados dentro do Web Worker,
retornam erro de CORS, por se tratar
de um fetch() direto do browser.
Por isso, precisamos evoluir nossas configurações de resolveUrl que consequentemente
consiste na criação de um Reverse Proxy.
Como descrito aqui na documentação, essa configuração é usada para decidir de onde o Script será pego, ou seja, conseguimos desenvolver uma lógica para que dada url, usamos nosso proxy reverso para resolver de onde será pego o conteúdo do script JS, assim resolvendo o problema de CORS.
Exemplo:
.js
partytown = {
resolveUrl: (url, location) => {
const reverseProxyUrl = 'https://reverse-proxy';
const dnsToProxy = {
'https://example.com': reverseProxyUrl,
};
if(dnsToProxy[url.hostname]) {
return new URL(dnsToProxy[url.hostname]);
}
return url;
}
}Para ter uma configuração totalmente completa, precisamos construir um Proxy Reverso.
Pois, como dissemos anteriormente, quando carregamos os scripts dentro de um Web Worker,
estamos utilizando puramente uma request HTTP com fetch(), as requests precisam conter os headers corretos
de CORS.
Existem inúmeras formas de se criar um Reverse Proxy, na própria documentação do PartyTown tem exemplos.

Recomendamos que não! Principalmente Scripts que fazem muitas leituras no DOM. Por exemplo, bibliotecas de heatmap, como Hotjar.
Como já alertado na documentação oficial, pode acabar gerando um lag nas interações da página.
Sendo assim, estamos buscando uma forma mais performática de como mantermos esses scripts de heatmap no site, porém sem penalizar tanto nosso score de web core vitals.
Exemplo do score sem bibliotecas de heatmap.
