React v18.0
March 29, 2022 by The React Team
O React 18 agora está disponível no npm! No nosso último post, compartilhamos instruções passo a passo para atualizar seu aplicativo para o React 18. Neste post, daremos uma visão geral do que há de novo no React 18 e o que isso significa para o futuro.
Nossa última versão principal inclui melhorias imediatas como o batching automático, novas APIs como startTransition e renderização do lado do servidor com streaming com suporte a Suspense.
Muitos dos recursos do React 18 são baseados em nosso novo renderizador concorrente, uma alteração nos bastidores que desbloqueia novos recursos poderosos. O React Concorrente é opcional — ele só é habilitado quando você usa um recurso concorrente — mas achamos que terá um grande impacto na forma como as pessoas constroem aplicativos.
Passamos anos pesquisando e desenvolvendo suporte para concorrência no React e tivemos cuidado extra para fornecer um caminho de adoção gradual para usuários existentes. No verão passado, formamos o React 18 Working Group para reunir feedback de especialistas da comunidade e garantir uma experiência de atualização tranquila para todo o ecossistema React.
Caso você tenha perdido, compartilhamos grande parte dessa visão no React Conf 2021:
- Na keynote, explicamos como o React 18 se encaixa em nossa missão de facilitar o desenvolvimento de ótimas experiências do usuário
- Shruti Kapoor demonstrou como usar os novos recursos do React 18
- Shaundai Person nos deu uma visão geral de renderização do servidor com streaming com Suspense
Abaixo está uma visão geral completa do que esperar nesta versão, começando com Renderização Concorrente.
O que é React Concorrente?
A adição mais importante no React 18 é algo que esperamos que você nunca precise pensar: a concorrência. Achamos que isso é em grande parte verdade para os desenvolvedores de aplicativos, embora a história possa ser um pouco mais complicada para os mantenedores de bibliotecas.
A concorrência não é um recurso, em si. É um novo mecanismo nos bastidores que permite ao React preparar várias versões da sua UI ao mesmo tempo. Você pode pensar na concorrência como um detalhe de implementação — é valioso por causa dos recursos que ele desbloqueia. O React usa técnicas sofisticadas em sua implementação interna, como filas de prioridade e múltiplos buffering. Mas você não verá esses conceitos em nenhuma de nossas APIs públicas.
Quando projetamos APIs, tentamos esconder os detalhes de implementação dos desenvolvedores. Como desenvolvedor React, você se concentra em o que deseja que a experiência do usuário pareça e o React lida com como oferecer essa experiência. Portanto, não esperamos que os desenvolvedores React saibam como a concorrência funciona por dentro.
No entanto, o React Concorrente é mais importante do que um detalhe de implementação típico — é uma atualização fundamental para o modelo de renderização principal do React. Então, embora não seja super importante saber como a concorrência funciona, pode valer a pena saber o que é em alto nível.
Uma propriedade chave do React Concorrente é que a renderização é interrompível. Quando você atualiza pela primeira vez para o React 18, antes de adicionar quaisquer recursos concorrentes, as atualizações são renderizadas da mesma forma que nas versões anteriores do React — em uma única transação síncrona e ininterrupta. Com a renderização síncrona, assim que uma atualização começa a renderizar, nada pode interrompê-la até que o usuário possa ver o resultado na tela.
Em uma renderização concorrente, este nem sempre é o caso. O React pode começar a renderizar uma atualização, pausar no meio e, em seguida, continuar mais tarde. Pode até abandonar completamente uma renderização em andamento. O React garante que a UI aparecerá consistente, mesmo que uma renderização seja interrompida. Para fazer isso, ele espera para realizar mutações do DOM até o final, depois que toda a árvore tiver sido avaliada. Com essa capacidade, o React pode preparar novas telas em segundo plano sem bloquear a thread principal. Isso significa que a UI pode responder imediatamente à entrada do usuário, mesmo que esteja no meio de uma grande tarefa de renderização, criando uma experiência de usuário fluida.
Outro exemplo é o estado reutilizável. O React Concorrente pode remover seções da UI da tela e, em seguida, adicioná-las de volta mais tarde, reutilizando o estado anterior. Por exemplo, quando um usuário sai de uma tela e volta, o React deve ser capaz de restaurar a tela anterior no mesmo estado em que estava antes. Em uma próxima versão secundária, estamos planejando adicionar um novo componente chamado <Offscreen>
que implementa esse padrão. Da mesma forma, você poderá usar o Offscreen para preparar uma nova UI em segundo plano para que ela esteja pronta antes que o usuário a revele.
A renderização concorrente é uma nova e poderosa ferramenta no React e a maioria de nossos novos recursos são criados para tirar proveito dela, incluindo Suspense, transitions e renderização do servidor com streaming. Mas o React 18 é apenas o começo do que pretendemos construir sobre essa nova fundação.
Adotando Gradualmente Recursos Concorrentes
Tecnicamente, a renderização concorrente é uma mudança radical. Como a renderização concorrente é interrompível, os componentes se comportam de maneira um pouco diferente quando está habilitada.
Em nossos testes, atualizamos milhares de componentes para o React 18. O que descobrimos é que quase todos os componentes existentes “simplesmente funcionam” com renderização concorrente, sem nenhuma alteração. No entanto, alguns deles podem exigir algum esforço de migração adicional. Embora as alterações geralmente sejam pequenas, você ainda terá a capacidade de fazê-las no seu próprio ritmo. O novo comportamento de renderização no React 18 é habilitado apenas nas partes do seu aplicativo que usam novos recursos.
A estratégia geral de atualização é fazer com que seu aplicativo seja executado no React 18 sem quebrar o código existente. Então você pode gradualmente começar a adicionar recursos concorrentes no seu próprio ritmo. Você pode usar <StrictMode>
para ajudar a expor erros relacionados à concorrência durante o desenvolvimento. O Modo Strict não afeta o comportamento de produção, mas durante o desenvolvimento ele registrará avisos extras e invocará funções duplas que deveriam ser idempotentes. Ele não pegará tudo, mas é eficaz na prevenção dos tipos mais comuns de erros.
Depois de atualizar para o React 18, você poderá começar a usar os recursos concorrentes imediatamente. Por exemplo, você pode usar startTransition para navegar entre as telas sem bloquear a entrada do usuário. Ou use useDeferredValue para limitar as re-renderizações caras.
No entanto, a longo prazo, esperamos que a principal forma de adicionar concorrência ao seu aplicativo seja usando uma biblioteca ou framework habilitado para concorrência. Na maioria dos casos, você não interagirá com as APIs concorrentes diretamente. Por exemplo, em vez de os desenvolvedores chamarem startTransition sempre que navegarem para uma nova tela, as bibliotecas de roteamento automaticamente encapsularão as navegações em startTransition.
Pode levar algum tempo para que as bibliotecas sejam atualizadas para serem compatíveis com a concorrência. Fornecemos novas APIs para facilitar o uso de recursos concorrentes pelas bibliotecas. Enquanto isso, seja paciente com os mantenedores, pois estamos trabalhando para migrar gradualmente o ecossistema React.
Para mais informações, veja nosso post anterior: Como atualizar para o React 18.
Suspense em Data Frameworks
No React 18, você pode começar a usar Suspense para busca de dados em frameworks opinativos como Relay, Next.js, Hydrogen ou Remix. A busca de dados ad hoc com Suspense é tecnicamente possível, mas ainda não é recomendada como uma estratégia geral.
No futuro, podemos expor primitivos adicionais que podem facilitar o acesso aos seus dados com Suspense, talvez sem o uso de um framework opinativo. No entanto, o Suspense funciona melhor quando está profundamente integrado à arquitetura do seu aplicativo: seu roteador, sua camada de dados e seu ambiente de renderização do servidor. Portanto, mesmo a longo prazo, esperamos que as bibliotecas e os frameworks desempenhem um papel crucial no ecossistema React.
Como nas versões anteriores do React, você também pode usar Suspense para divisão de código no cliente com React.lazy. Mas nossa visão para Suspense sempre foi sobre muito mais do que carregar código — o objetivo é estender o suporte para Suspense para que, eventualmente, o mesmo fallback Suspense declarativo possa lidar com qualquer operação assíncrona (carregamento de código, dados, imagens, etc.).
Server Components ainda está em desenvolvimento
Server Components é um recurso futuro que permite aos desenvolvedores criar aplicativos que abrangem o servidor e o cliente, combinando a rica interatividade de aplicativos client-side com o desempenho aprimorado da renderização tradicional do servidor. Server Components não está inerentemente acoplado ao React Concorrente, mas foi projetado para funcionar melhor com recursos concorrentes, como Suspense e renderização do servidor com streaming.
Server Components ainda é experimental, mas esperamos lançar uma versão inicial em um lançamento secundário 18.x. Enquanto isso, estamos trabalhando com frameworks como Next.js, Hydrogen e Remix para avançar na proposta e prepará-la para ampla adoção.
Novidades no React 18
Novo Recurso: Batching Automático
Batching é quando o React agrupa várias atualizações de state em uma única re-renderização para obter melhor desempenho. Sem batching automático, só fizemos batch atualizações dentro dos manipuladores de eventos do React. As atualizações dentro de promises, setTimeout, manipuladores de eventos nativos ou qualquer outro evento não foram batch por padrão no React. Com o batching automático, essas atualizações serão batch automaticamente:
// Before: only React events were batched.
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will render twice, once for each state update (no batching)
}, 1000);
// After: updates inside of timeouts, promises,
// native event handlers or any other event are batched.
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that's batching!)
}, 1000);
Para mais informações, consulte esta postagem para Batching automático para menos renders no React 18.
Novo Recurso: Transitions
Uma transition é um novo conceito no React para distinguir entre atualizações urgentes e não urgentes.
- Atualizações urgentes refletem a interação direta, como digitar, clicar, pressionar e assim por diante.
- Atualizações de transition fazem a transição da UI de uma visualização para outra.
Atualizações urgentes como digitação, clique ou pressione, precisam de resposta imediata para corresponder às nossas intuições sobre como os objetos físicos se comportam. Caso contrário, eles parecem “errados”. No entanto, as transitions são diferentes porque o usuário não espera ver cada valor intermediário na tela.
Por exemplo, quando você seleciona um filtro em um dropdown, espera que o próprio botão de filtro responda imediatamente quando você clica. No entanto, os resultados reais podem fazer a transição separadamente. Um pequeno atraso seria imperceptível e geralmente esperado. E se você alterar o filtro novamente antes que os resultados terminem de renderizar, você só se importa em ver os resultados mais recentes.
Normalmente, para a melhor experiência do usuário, uma única entrada do usuário deve resultar em uma atualização urgente e uma não urgente. Você pode usar a API startTransition dentro de um evento de entrada para informar ao React quais atualizações são urgentes e quais são “transitions”:
import { startTransition } from 'react';
// Urgent: Show what was typed
setInputValue(input);
// Mark any state updates inside as transitions
startTransition(() => {
// Transition: Show the results
setSearchQuery(input);
});
Atualizações encapsuladas em startTransition são tratadas como não urgentes e serão interrompidas se atualizações mais urgentes, como cliques ou pressionamentos de teclas, chegarem. Se uma transition for interrompida pelo usuário (por exemplo, digitando vários caracteres em sequência), o React descartará o trabalho de renderização obsoleto que não foi finalizado e renderizará apenas a atualização mais recente.
useTransition
: um Hook para iniciar transitions, incluindo um valor para rastrear o estado pendente.startTransition
: um método para iniciar transitions quando o Hook não pode ser usado.
As transitions optarão pela renderização concorrente, que permite que a atualização seja interrompida. Se o conteúdo re-suspender, as transitions também dizem ao React para continuar mostrando o conteúdo atual enquanto renderiza o conteúdo da transition em segundo plano (consulte o Suspense RFC para obter mais informações).
Consulte a documentação para transitions aqui.
Novos Recursos do Suspense
Suspense permite que você especifique declarativamente o estado de carregamento para uma parte da árvore de componentes, se ela ainda não estiver pronta para ser exibida:
<Suspense fallback={<Spinner />}>
<Comments />
</Suspense>
Suspense torna o “estado de carregamento da UI” um conceito declarativo de primeira classe no modelo de programação React. Isso nos permite construir recursos de nível superior em cima dele.
Apresentamos uma versão limitada do Suspense há vários anos. No entanto, o único caso de uso suportado foi a divisão de código com React.lazy, e não foi suportado ao renderizar no servidor.
No React 18, adicionamos suporte para Suspense no servidor e expandimos seus recursos usando recursos de renderização concorrente.
Suspense no React 18 funciona melhor quando combinado com a API de transition. Se você suspender durante uma transition, o React impedirá que o conteúdo já visível seja substituído por um fallback. Em vez disso, o React atrasará a renderização até que dados suficientes tenham sido carregados para evitar um estado de carregamento ruim.
Para mais informações, consulte o RFC para Suspense no React 18.
Novas APIs de renderização do cliente e do servidor
Nesta versão, aproveitamos a oportunidade para redesenhar as APIs que expomos para renderização no cliente e no servidor. Essas alterações permitem que os usuários continuem a usar as APIs antigas no modo React 17 enquanto atualizam para as novas APIs no React 18.
React DOM Client
Essas novas APIs agora são exportadas de react-dom/client
:
createRoot
: Novo método para criar uma raiz pararender
ouunmount
. Use-o em vez deReactDOM.render
. Os novos recursos no React 18 não funcionam sem ele.hydrateRoot
: Novo método para hidratar um aplicativo renderizado no servidor. Use-o em vez deReactDOM.hydrate
em conjunto com as novas APIs do React DOM Server. Os novos recursos no React 18 não funcionam sem ele.
createRoot
e hydrateRoot
aceitam uma nova opção chamada onRecoverableError
caso você queira ser notificado quando o React se recuperar de erros durante a renderização ou hidratação para registro. Por padrão, o React usará reportError
, ou console.error
nos navegadores mais antigos.
Veja a documentação do React DOM Client aqui.
React DOM Server
Essas novas APIs agora são exportadas de react-dom/server
e têm suporte total para streaming Suspense no servidor:
renderToPipeableStream
: para streaming em ambientes Node.renderToReadableStream
: para ambientes de tempo de execução de borda modernos, como Deno e Cloudflare workers.
O método renderToString
existente continua funcionando, mas não é recomendado.
Veja a documentação do React DOM Server aqui.
Novos Comportamentos do Modo Strict
No futuro, gostaríamos de adicionar um recurso que permita ao React adicionar e remover seções da UI, preservando o estado. Por exemplo, quando um usuário sai de uma tela e volta, o React deve ser capaz de mostrar imediatamente a tela anterior. Para fazer isso, o React desmontaria e remontaria árvores usando o mesmo estado do componente de antes.
Esse recurso dará aos aplicativos React melhor desempenho imediato, mas requer que os componentes sejam resilientes a efeitos sendo montados e destruídos várias vezes. A maioria dos efeitos funcionará sem nenhuma alteração, mas alguns efeitos assumem que eles só são montados ou destruídos uma vez.
Para ajudar a expor esses problemas, o React 18 apresenta uma nova verificação somente para desenvolvimento no Strict Mode. Essa nova verificação desmontará e remontará automaticamente cada componente, sempre que um componente montar pela primeira vez, restaurando o estado anterior na segunda montagem.
Antes dessa alteração, o React montaria o componente e criaria os efeitos:
* React monta o componente.
* Layout effects são criados.
* Effects são criados.
Com o Modo Strict no React 18, o React irá simular a desmontagem e remontagem do componente no modo de desenvolvimento:
* React monta o componente.
* Layout effects são criados.
* Effects são criados.
* React simula a desmontagem do componente.
* Layout effects são destruídos.
* Effects são destruídos.
* React simula a montagem do componente com o estado anterior.
* Layout effects são criados.
* Effects são criados.
Veja a documentação para garantir o estado reutilizável aqui.
Novos Hooks
useId
useId
é um novo Hook para gerar IDs únicos tanto no cliente quanto no servidor, evitando incompatibilidades de hidratação. Ele é útil principalmente para bibliotecas de componentes que se integram com APIs de acessibilidade que exigem IDs únicos. Isso resolve um problema que já existe no React 17 e versões anteriores, mas é ainda mais importante no React 18 por causa de como o novo renderizador de servidor de streaming entrega o HTML fora de ordem. Veja a documentação aqui.
Note
useId
não serve para gerar chaves em uma lista. As chaves devem ser geradas a partir de seus dados.
useTransition
useTransition
e startTransition
permitem que você marque algumas atualizações de estado como não urgentes. Outras atualizações de estado são consideradas urgentes por padrão. O React permitirá que atualizações de estado urgentes (por exemplo, atualizar uma entrada de texto) interrompam atualizações de estado não urgentes (por exemplo, renderizar uma lista de resultados de pesquisa). Veja a documentação aqui.
useDeferredValue
useDeferredValue
permite que você adie a re-renderização de uma parte não urgente da árvore. É semelhante ao debouncing, mas tem algumas vantagens em comparação a ele. Não há atraso de tempo fixo, então o React tentará a renderização adiada logo após a primeira renderização ser refletida na tela. A renderização adiada é interrompível e não bloqueia a entrada do usuário. Veja a documentação aqui.
useSyncExternalStore
useSyncExternalStore
é um novo Hook que permite que lojas externas suportem leituras simultâneas, forçando as atualizações da loja a serem síncronas. Ele remove a necessidade de useEffect ao implementar assinaturas em fontes de dados externas e é recomendado para qualquer biblioteca que se integra com estado externo ao React. Veja a documentação aqui.
Note
useSyncExternalStore
destina-se a ser usado por bibliotecas, não código de aplicação.
useInsertionEffect
useInsertionEffect
é um novo Hook que permite que bibliotecas CSS-in-JS abordem problemas de desempenho de injeção de estilos em renderização. A menos que você já tenha construído uma biblioteca CSS-in-JS, não esperamos que você use isso. Este Hook será executado após o DOM ser mutado, mas antes que os layout effects leiam o novo layout. Isso resolve um problema que já existe no React 17 e versões anteriores, mas é ainda mais importante no React 18 porque o React cede ao navegador durante a renderização concorrente, dando a ele a chance de recalcular o layout. Veja a documentação aqui.
Note
useInsertionEffect
destina-se a ser usado por bibliotecas, não código de aplicação.
Como Atualizar
Veja Como Atualizar para o React 18 para obter instruções passo a passo e uma lista completa de mudanças significativas e notáveis.
Changelog
React
- Adiciona
useTransition
euseDeferredValue
para separar atualizações urgentes de transições. (#10426, #10715, #15593, #15272, #15578, #15769, #17058, #18796, #19121, #19703, #19719, #19724, #20672, #20976 por @acdlite, @lunaruan, @rickhanlonii, e @sebmarkbage) - Adiciona
useId
para gerar IDs únicos. (#17322, #18576, #22644, #22672, #21260 por @acdlite, @lunaruan, e @sebmarkbage) - Adiciona
useSyncExternalStore
para ajudar bibliotecas de lojas externas a se integrarem com o React. (#15022, #18000, #18771, #22211, #22292, #22239, #22347, #23150 por @acdlite, @bvaughn, e @drarmstr) - Adiciona
startTransition
como uma versão deuseTransition
sem feedback pendente. (#19696 por @rickhanlonii) - Adiciona
useInsertionEffect
para bibliotecas CSS-in-JS. (#21913 por @rickhanlonii) - Faz com que Suspense remonte os layout effects quando o conteúdo reaparece. (#19322, #19374, #19523, #20625, #21079 por @acdlite, @bvaughn, e @lunaruan)
- Faz com que
<StrictMode>
re-execute os effects para verificar o estado restaurável. (#19523 , #21418 por @bvaughn e @lunaruan) - Assume que Symbols estão sempre disponíveis. (#23348 por @sebmarkbage)
- Remove o polyfill
object-assign
. (#23351 por @sebmarkbage) - Remove a API
unstable_changedBits
sem suporte. (#20953 por @acdlite) - Permite que os componentes renderizem undefined. (#21869 por @rickhanlonii)
- Esvazia
useEffect
resultantes de eventos discretos como cliques de forma síncrona. (#21150 por @acdlite) - Suspense
fallback={undefined}
agora se comporta da mesma forma quenull
e não é ignorado. (#21854 por @rickhanlonii) - Considera todos os
lazy()
que resolvem para o mesmo componente equivalentes. (#20357 por @sebmarkbage) - Não corrige o console durante a primeira renderização. (#22308 por @lunaruan)
- Melhora o uso de memória. (#21039 por @bgirard)
- Melhora as mensagens se a coerção de string lançar (Temporal.*, Symbol, etc.) (#22064 por @justingrant)
- Usa
setImmediate
quando disponível no lugar deMessageChannel
. (#20834 por @gaearon) - Corrige o contexto que falha ao propagar dentro de árvores suspensas. (#23095 por @gaearon)
- Corrige
useReducer
observando props incorretas removendo o mecanismo de saída imediata. (#22445 por @josephsavona) - Corrige
setState
sendo ignorado no Safari ao anexar iframes. (#23111 por @gaearon) - Corrige uma falha ao renderizar
ZonedDateTime
na árvore. (#20617 por @dimaqq) - Corrige uma falha quando o documento é definido como
null
em testes. (#22695 por @SimenB) - Corrige
onLoad
não sendo acionado quando os recursos concorrentes estão ativados. (#23316 por @gnoff) - Corrige um aviso quando um seletor retorna
NaN
. (#23333 por @hachibeeDI) - Corrige uma falha quando o documento é definido como
null
em testes. (#22695 por @SimenB) - Corrige o cabeçalho de licença gerado. (#23004 por @vitaliemiron)
- Adiciona
package.json
como um dos pontos de entrada. (#22954 por @Jack) - Permite suspender fora de uma fronteira de Suspense. (#23267 por @acdlite)
- Registra um erro recuperável sempre que a hidratação falha. (#23319 por @acdlite)
React DOM
- Adiciona
createRoot
ehydrateRoot
. (#10239, #11225, #12117, #13732, #15502, #15532, #17035, #17165, #20669, #20748, #20888, #21072, #21417, #21652, #21687, #23207, #23385 por @acdlite, @bvaughn, @gaearon, @lunaruan, @rickhanlonii, @trueadm, e @sebmarkbage) - Adiciona hidratação seletiva. (#14717, #14884, #16725, #16880, #17004, #22416, #22629, #22448, #22856, #23176 por @acdlite, @gaearon, @salazarm, e @sebmarkbage)
- Adiciona
aria-description
à lista de atributos ARIA conhecidos. (#22142 por @mahyareb) - Adiciona o evento
onResize
a elementos de vídeo. (#21973 por @rileyjshaw) - Adiciona
imageSizes
eimageSrcSet
às props conhecidas. (#22550 por @eps1lon) - Permite filhos
<option>
não-string sevalue
for fornecido. (#21431 por @sebmarkbage) - Corrige o estilo
aspectRatio
não sendo aplicado. (#21100 por @gaearon) - Avisa se
renderSubtreeIntoContainer
for chamado. (#23355 por @acdlite)
React DOM Server
- Adiciona o novo renderizador de streaming. (#14144, #20970, #21056, #21255, #21200, #21257, #21276, #22443, #22450, #23247, #24025, #24030 por @sebmarkbage)
- Corrige provedores de contexto em SSR ao lidar com várias solicitações. (#23171 por @frandiox)
- Volta para renderização do cliente em incompatibilidade de texto. (#23354 por @acdlite)
- Deprecia
renderToNodeStream
. (#23359 por @sebmarkbage) - Corrige um erro de log falso no novo renderizador do servidor. (#24043 por @eps1lon)
- Corrige um bug no novo renderizador do servidor. (#22617 por @shuding)
- Ignora valores de função e símbolo dentro de elementos personalizados no servidor. (#21157 por @sebmarkbage)
React DOM Test Utils
- Lança um erro quando
act
é usado em produção. (#21686 por @acdlite) - Suporta a desabilitação de avisos de act falsos com
global.IS_REACT_ACT_ENVIRONMENT
. (#22561 por @acdlite) - Expande o aviso act para cobrir todas as APIs que podem agendar o trabalho React. (#22607 por @acdlite)
- Faz com que
act
agrupe atualizações. (#21797 por @acdlite) - Remove o aviso para efeitos passivos pendentes. (#22609 por @acdlite)
React Refresh
- Rastreia as raízes montadas tardiamente no Fast Refresh. (#22740 por @anc95)
- Adiciona o campo
exports
aopackage.json
. (#23087 por @otakustay)
Server Components (Experimental)
- Adiciona suporte ao Contexto do Servidor. (#23244 por @salazarm)
- Adiciona suporte
lazy
. (#24068 por @gnoff) - Atualiza o plugin webpack para webpack 5 (#22739 por @michenly)
- Corrige um erro no carregador do Node. (#22537 por @btea)
- Usa
globalThis
em vez dewindow
para ambientes de borda. (#22777 por @huozhi)