React Native Nova Arquitetura: Migração e Medição de Performance para Produção

O React Native nos permite construir interfaces móveis nativas com React, mas a forma como JavaScript se comunica com a plataforma afeta o que essas interfaces podem fazer. A Nova Arquitetura muda essa base: como as funções nativas são chamadas, como as visualizações são renderizadas e como as atualizações são agendadas.
Para desenvolvedores mantendo um aplicativo mais antigo, compreender essas mudanças ajuda a responder três questões práticas: por que migrar, o que precisa ser atualizado e como podemos dizer se a migração melhorou o aplicativo?
Quando a Nova Arquitetura se tornou disponível?
Existem três marcos distintos:
Release React Native | O que mudou |
|---|---|
React Native 0.68 (2022) | A Nova Arquitetura ficou disponível para ativação opcional em caráter experimental. |
React Native 0.76 (2024) | Tornou-se o padrão e foi declarada pronta para uso em produção. |
React Native 0.82+ (2025) | O React Native passou a funcionar exclusivamente com a Nova Arquitetura. Não é mais possível desativá-la. |
Esses são estágios diferentes de adoção: disponibilidade inicial, maturidade para uso em produção e obrigatoriedade. Fontes: anúncio da versão 0.68, anúncio da versão 0.76 e anúncio da versão 0.82.
O Expo tem seus próprios marcos. O SDK 52 habilitou a Nova Arquitetura para projetos recém-criados; os SDKs 53 e 54 passaram a habilitá-la por padrão. O SDK 54 é o último SDK do Expo que permite desativá-la. O SDK 55 e as versões posteriores exigem seu uso. O SDK 55 utiliza o React Native 0.83. Guia de arquitetura do Expo
Como a arquitetura antiga funcionava

Figura 1. O modelo de comunicação da arquitetura legada, baseado na bridge. O JavaScriptCore aparece como exemplo; aplicativos legados também podiam usar o Hermes. O Metro faz parte do processo de build, e “JSON” é uma forma abreviada de se referir às mensagens serializadas da bridge.
Na arquitetura legada, frequentemente associada ao renderizador Paper, JavaScript e código nativo geralmente se comunicavam através de um bridge assíncrono em lote. As chamadas e seus argumentos eram convertidos em mensagens serializáveis, enfileiradas e processadas do outro lado.
Por exemplo, JavaScript poderia solicitar uma operação nativa e receber seu resultado mais tarde através de um callback ou Promise. Essa separação era útil, mas mensagens frequentes, grandes payloads e coordenação entre JavaScript e UI nativa introduziam overhead.
A limitação mais profunda era timing. Algumas interações precisam de informações de layout imediatas ou uma atualização de UI urgente. Um caminho de comunicação assíncrono tornava essas interações difíceis e podia produzir estados intermediários visíveis, como um tooltip aparecendo brevemente na posição errada. Essas restrições também impediam suporte completo aos recursos de renderização concorrente do React. Explicação do React Native sobre a reformulação
Chamar isso de “bridge JSON” é uma simplificação útil, mas evite imaginar cada chamada como uma string JSON enviada por um canal semelhante a uma rede. A questão arquitetural é a comunicação serializada e enfileirada. Evite também a afirmação absoluta de que os módulos nativos legados nunca podiam expor métodos síncronos: existiam APIs bloqueantes específicas, embora as chamadas assíncronas fossem o modelo habitual. Métodos síncronos legados
Compreendendo os componentes e seu efeito na performance

Figura 2. Uma visão geral da Nova Arquitetura. Os rótulos das threads são simplificados: o Metro é executado durante o desenvolvimento e os builds, o trabalho do Fabric pode abranger várias threads e os TurboModules não compartilham uma única thread nativa universal. A escolha do Hermes como engine é independente. O JSI evita a bridge legada, mas não elimina todos os custos de conversão ou cópia.
Esses nomes descrevem responsabilidades diferentes. O Hermes executa JavaScript, o JSI conecta o ambiente de execução ao código nativo, o Fabric gerencia a renderização e os TurboModules expõem funcionalidades nativas. O Codegen fornece código de integração tipado durante o build. Entender essas responsabilidades ajuda a explicar de onde pode vir uma melhoria de desempenho.

Hermes: o motor que executa seu JavaScript
Uma engine executa o JavaScript dos seus componentes, manipuladores de eventos e regras de negócio. O Hermes é a engine JavaScript de código aberto da Meta, otimizada para React Native, com foco na inicialização, no uso de memória e no tamanho do aplicativo.
Em um build de produção típico com Hermes, o JavaScript é compilado em bytecode durante o build. Bytecode é um formato de instruções que a engine consegue executar; não é código de máquina nativo. Prepará-lo antes da instalação reduz o trabalho de análise e compilação de JavaScript necessário quando o usuário abre o aplicativo. Isso pode reduzir o tempo de inicialização. O Hermes também pode usar menos memória que o JavaScriptCore, embora o resultado dependa do aplicativo. Como usar o Hermes
Imagine abrir um aplicativo apenas para verificar o status de uma entrega. Reduzir o trabalho de preparação da engine pode ajudar a primeira tela útil a aparecer mais cedo. Isso não reduz o tempo de resposta do servidor nem elimina cálculos custosos do aplicativo.
O Hermes é independente da Nova Arquitetura: aplicativos legados já podiam utilizá-lo. Se tanto o build de referência quanto o build migrado usam a mesma versão e as mesmas configurações do Hermes, a compilação em bytecode já está presente nos dois; ela não é um novo ganho causado pela ativação do Fabric.
O que medir: inicialização a frio, tempo de inicialização do JavaScript e memória do processo. Mantenha a configuração da engine constante ao medir a migração de arquitetura em si.
JSI: como o JavaScript acessa o código nativo
JSI significa JavaScript Interface. É uma interface C++ para um ambiente de execução JavaScript, que permite à integração nativa expor funções e objetos ao JavaScript. Não é uma engine nem uma thread.
A bridge legada geralmente empacotava as solicitações nativas em mensagens serializadas e enfileiradas. A integração baseada em JSI pode evitar esse caminho. Eliminar o empacotamento de mensagens e o agendamento da bridge pode reduzir a sobrecarga da comunicação entre JavaScript e código nativo, especialmente em chamadas frequentes.
Um pequeno getter nativo ilustra a diferença: se a API expõe um método síncrono, o JavaScript pode receber seu valor diretamente, em vez de agendar um callback para uma resposta posterior da bridge. Métodos assíncronos continuam fazendo parte desse sistema. Comunicação nativa na Nova Arquitetura
Essa distinção importa porque menos sobrecarga de comunicação não significa menos trabalho dentro da operação. Ler um valor em cache e processar uma imagem grande têm custos muito diferentes. Tornar a operação de imagem síncrona poderia bloquear o JavaScript durante toda a sua execução. O JSI também não garante transferência de dados sem cópia; as conversões e alocações dependem da implementação da API.
O que medir: a latência de chamadas nativas representativas, a quantidade de chamadas em um fluxo do usuário e o tempo gasto convertendo dados em comparação com a execução do trabalho nativo em si.
Fabric: como as atualizações do React se tornam views nativas
O Fabric é o renderizador do React Native. Quando um componente retorna elementos como <View> e <Text>, o renderizador coordena as views nativas que vão representá-los na tela.
Seu processo tem três grandes fases:
1. Render: o React executa a lógica dos componentes, e o Fabric constrói uma shadow tree em C++ que descreve os componentes nativos.
2. Commit: o layout é calculado com o Yoga, e a próxima árvore é preparada.
3. Mount: as mudanças são aplicadas às views nativas reais.
A shadow tree é uma representação interna da interface, não a interface visível em si. O Fabric pode compartilhar partes que não mudaram entre versões da árvore, reduzindo as cópias necessárias para as atualizações. Ele também determina quais alterações nas views da plataforma são necessárias. Render, commit e mount
O Fabric melhora o desempenho e a capacidade de resposta por meio de uma coordenação mais eficiente das atualizações. Ele oferece suporte a prioridades diferentes e ao acesso síncrono ao layout, viabilizando recursos modernos de renderização do React. Por exemplo, um tooltip pode usar informações de layout para se posicionar antes de ser exibido, evitando um frame intermediário no lugar errado. O achatamento da hierarquia de views também pode evitar views nativas de agrupamento desnecessárias. Benefícios do Fabric
Considere uma tela de busca em que a digitação atualiza tanto um campo de entrada quanto uma lista grande de resultados. Com as atualizações do React agendadas adequadamente, a resposta urgente à digitação pode ter prioridade sobre a renderização dos resultados. Isso melhora a capacidade de resposta sem implicar que a quantidade total de trabalho seja menor. A renderização concorrente também não transfere automaticamente um cálculo pesado de filtragem para outra thread.
O Fabric não fica restrito a uma “thread do Fabric” dedicada. Grande parte do processo de renderização costuma ser executada na thread do JavaScript, enquanto as alterações nas views nativas acontecem na thread da interface; atualizações urgentes podem seguir outros caminhos suportados. Modelo de threads
O que medir: a latência entre a entrada do usuário e a resposta na tela, os frames perdidos durante as interações e os saltos visíveis de layout. Uma resposta mais fluida sob carga pode ser relevante mesmo quando o tempo médio de renderização quase não muda.
TurboModules: recursos nativos carregados sob demanda
Um módulo nativo expõe funcionalidades da plataforma ao JavaScript: armazenamento, informações do dispositivo ou acesso a um SDK nativo, por exemplo. Os TurboModules são o novo sistema do React Native para implementar essas interfaces.
Eles oferecem suporte à inicialização sob demanda: um módulo pode ser inicializado quando solicitado pela primeira vez, em vez de acrescentar esse trabalho à inicialização do aplicativo. Se a tela inicial não usa um determinado recurso nativo, adiar a inicialização do módulo pode reduzir o trabalho de inicialização e o uso inicial de memória. Os TurboModules também utilizam a nova infraestrutura de comunicação nativa. Novos módulos nativos
Por exemplo, suponha que um aplicativo só solicite um módulo de digitalização de documentos quando o usuário abrir a tela de digitalização. Adiar a inicialização desse módulo pode beneficiar a abertura do aplicativo. Esse é um cenário ilustrativo: o benefício depende da implementação da biblioteca e de o código do aplicativo acessá-la antecipadamente ou não.
Há uma contrapartida: a inicialização adiada pode aparecer como latência adicional na primeira utilização do recurso. O carregamento sob demanda não remove automaticamente o módulo do binário instalado, e os módulos solicitados na abertura do aplicativo ainda precisam ser inicializados naquele momento.
As APIs dos TurboModules podem expor métodos síncronos ou resultados assíncronos. Sua especificação tipada descreve essa interface, enquanto o código nativo implementa a operação. Uma captura de câmera não deve se tornar síncrona simplesmente porque o acesso síncrono está disponível. [Guia de implementação de TurboModules](https://reactnative.dev/docs/turbo-native-modules-introduction)
O que medir: inicialização, memória inicial, primeira utilização de um recurso nativo e utilizações posteriores. Meça os dois lados dessa escolha de inicialização.

Figure 4. Build-time preparation and runtime access are separate. Codegen generates interfaces; developers supply the native implementation. Initialization can occur when the module is first requested, including during import.
Um pequeno exemplo de TurboModule
Este módulo ilustrativo retorna um rótulo de status armazenado em cache. Sua especificação TypeScript declara o método nativo:
// specs/NativeStatus.ts
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
getLabel(): string;
}
export default TurboModuleRegistry.getEnforcing<Spec>(
'NativeStatus',
);O código do aplicativo pode então chamá-lo:
import NativeStatus from './specs/NativeStatus';
const label = NativeStatus.getLabel(); // Ex.: "Ready"getEnforcing solicita o módulo registrado e lança uma exceção se ele não estiver disponível. Como é executado no nível superior do arquivo de especificação, importar esse arquivo pode disparar a solicitação; a inicialização sob demanda não espera necessariamente até que getLabel() seja chamado.
O tipo de retorno string descreve uma API síncrona. A implementação nativa deve retornar rapidamente seu valor em cache, porque quem fez a chamada espera a conclusão. Já um método assíncrono declararia um resultado Promise e usaria uma implementação nativa apropriada.
Este é um exemplo de interface e uso, não um módulo completo pronto para execução. Ele também precisa de configuração do Codegen, implementação nativa, registro e um novo build nativo. Colar esses trechos não o tornará disponível no Expo Go. A estrutura segue o guia de TurboModules do React Native.
Na Figura 4, a chamada ao registro corresponde às etapas 1–2; getLabel() corresponde às etapas 3–4. A possível economia vem do adiamento da preparação e da redução da sobrecarga da bridge, não da chamada TypeScript em si.
Codegen: o contrato entre JavaScript e código nativo
O Codegen é executado durante o build. Ele lê especificações TypeScript ou Flow compatíveis e gera código de suporte em C++ e código de integração específico de cada plataforma. A implementação nativa deve seguir essas interfaces geradas. Documentação do Codegen
Isso cria um contrato consistente para argumentos de métodos, valores de retorno e propriedades de componentes. Seus principais benefícios práticos são a correção da integração e a redução do código de conexão escrito manualmente. Ele dá suporte à nova infraestrutura, mas não deve ser apresentado como uma promessa independente de lógica de aplicativo mais rápida.
Como as peças funcionam juntas
Imagine abrir uma tela que lê uma preferência salva por meio de um TurboModule e exibe o resultado:
1. O Hermes executa o JavaScript da tela.
2. O aplicativo solicita o TurboModule, inicializando-o se necessário.
3. As vinculações baseadas em JSI conectam essa solicitação à implementação nativa, usando interfaces preparadas com o Codegen.
4. O valor retornado é usado para atualizar o estado do React.
5. O Fabric coordena a atualização correspondente da interface nativa.
Este é um exemplo conceitual, não um fluxo universal: o Fabric e os TurboModules têm finalidades diferentes, e a renderização não precisa passar por um TurboModule.
Componente | Possível melhoria | Limitação a verificar |
|---|---|---|
Hermes | Menos preparação da engine na abertura; menos memória em alguns aplicativos | Aplicativos que já usam Hermes já têm esses benefícios |
JSI | Menos sobrecarga na interação entre JavaScript e código nativo | Trabalho nativo, conversões e bloqueios ainda consomem tempo |
Fabric | Melhor agendamento de interações e coordenação do layout | JavaScript custoso e desenho nativo ainda podem causar atrasos |
TurboModules | A inicialização adiada reduz o trabalho na abertura | O primeiro uso pode arcar com o custo adiado |
Codegen | Integração nativa tipada e mais consistente | Não otimiza sua lógica de negócio |
Esses mecanismos explicam por que as melhorias são possíveis. O plano de medição apresentado mais adiante neste artigo determina quais delas realmente beneficiam seu aplicativo.
Onde o Metro entra?
O Metro faz parte do processo de build e desenvolvimento: ele resolve e transforma o código do aplicativo em um bundle. Em um build de produção com Hermes, esse bundle é então compilado em bytecode. O Metro não é um serviço executado dentro da thread JavaScript do aplicativo. O TypeScript está disponível em ambas as arquiteturas. Etapas de build do Metro
Aspecto | Arquitetura legada | Nova Arquitetura |
|---|---|---|
Comunicação nativa típica | Mensagens da bridge serializadas e processadas em lotes | Integração nativa por infraestrutura baseada em JSI |
Renderizador | Paper | Fabric |
Sistema de módulos nativos | Módulos nativos legados | TurboModules, com especificações tipadas para o Codegen |
Coordenação de atualizações | Limitada pela arquitetura assíncrona | Suporta coordenação síncrona e recursos de renderização concorrente |
Engine JavaScript | Podia usar Hermes ou JavaScriptCore | A escolha da engine é independente do conceito de arquitetura |
Desempenho esperado | Depende do aplicativo | Depende do aplicativo; as melhorias precisam ser medidas |
Como migrar um aplicativo Expo
1. Estabeleça uma referência e escolha um ponto de verificação da migração
Crie uma branch de migração e registre o SDK atual, a versão do React Native, as dependências e o desempenho dos principais fluxos.
Para um aplicativo que ainda usa a arquitetura legada, as orientações de migração do React Native recomendam o React Native 0.81 ou o Expo SDK 54 como ponto de transição. Habilite e valide a Nova Arquitetura nessa etapa antes de avançar para versões que já não suportam ambas. Trate isso como um ponto de verificação da migração, não como um destino de longo prazo. Orientações oficiais de migração
Atualize projetos Expo antigos um SDK por vez e siga as instruções de cada versão. Por exemplo, quando seu próximo destino for o SDK 54:
npm install expo@^54.0.0
npx expo install --fix
npx expo-doctor@latestUse o intervalo de versões correspondente ao SDK que você realmente pretende adotar. O processo de atualização do Expo também exige atualizar os projetos nativos e revisar as mudanças incompatíveis de cada versão. [Passo a passo de atualização do Expo](https://docs.expo.dev/workflow/upgrading-expo-sdk-walkthrough/)
2. Verifique as dependências nativas
O Expo Doctor verifica informações sobre bibliotecas no React Native Directory. Investigue pacotes incompatíveis, sem manutenção ou não testados, especialmente os que contêm código nativo. Um relatório sem problemas é um ponto de partida útil, não uma prova de que todos os recursos funcionam.
As camadas de interoperabilidade permitem que muitas bibliotecas legadas funcionem, mas a compatibilidade não é completa. Módulos criados com a Expo Modules API suportam a Nova Arquitetura por padrão; módulos nativos legados personalizados podem exigir trabalho adicional. [Orientações de compatibilidade do Expo
3. Habilite a Nova Arquitetura e gere um novo build
Na etapa de migração com o SDK 54, incorpore esta configuração à configuração existente do aplicativo:
{
"expo": {
"newArchEnabled": true
}
}No SDK 55 e nas versões posteriores, seu uso é obrigatório; remova qualquer configuração obsoleta `newArchEnabled: false`. Use um novo build nativo para testar a mudança. O Expo Go suporta apenas a Nova Arquitetura, portanto não permite comparar a arquitetura antiga com a nova. Orientações de configuração do Expo
Se você usa Continuous Native Generation e as pastas nativas podem ser reproduzidas a partir da configuração e dos plugins do aplicativo, gere-as novamente antes do build:
npx expo prebuild --clean
npx expo run:android
# Ou, no macOS com as ferramentas de desenvolvimento iOS:
npx expo run:ios--clean substitui os projetos nativos gerados. Se você mantém alterações manuais em ios ou android, aplique explicitamente as mudanças da atualização nativa em vez de apagar esses diretórios. Para builds com EAS, confira se os projetos nativos enviados ou a configuração gerada refletem a migração. Instruções de atualização dos projetos nativos
Uma atualização remota de JavaScript não pode substituir o ambiente de execução nativo de um aplicativo instalado: distribua um novo binário para essa migração e garanta que a versão de runtime do EAS Update diferencie ambientes de execução incompatíveis. Versões de runtime do Expo
4. Valide fluxos reais de uso
Teste navegação, gestos, listas, comportamento do teclado, modais, acessibilidade e integrações nativas nas duas plataformas. Dê atenção especial a views personalizadas, medições de layout, operações de câmera, mapas e comportamento em segundo plano.
Quando o build com SDK 54 funcionar com a Nova Arquitetura, continue atualizando gradualmente até o SDK com suporte que você pretende usar. Gere novos builds e repita os testes a cada etapa.
Quais benefícios esperar?
Os principais benefícios arquiteturais são recursos de renderização mais completos, melhor coordenação com a interface nativa e integração nativa tipada. O Fabric suporta recursos concorrentes e leituras síncronas de layout, enquanto o Codegen formaliza as interfaces entre JavaScript e código nativo. Benefícios do Fabric
Os ganhos de desempenho dependem do gargalo. Um aplicativo limitado pela sobrecarga das chamadas nativas pode se beneficiar de forma diferente de outro dominado pela decodificação de imagens, por atrasos de rede ou por JavaScript custoso. A inicialização sob demanda pode deslocar trabalho para depois da abertura do aplicativo, então meça também a primeira utilização de um recurso.
Evite afirmações universais como “a inicialização cai de três segundos para um” ou “a memória diminui 40%”. Elas exigem medições que identifiquem o aplicativo, o dispositivo, o build e o procedimento de teste. O carregamento de módulos sob demanda também não reduz automaticamente o bundle JavaScript.
Como identificar e medir os ganhos
Trate a migração como um experimento. O que segue é uma proposta de plano de medição, não um benchmark publicado do React Native.
Compare builds equivalentes
Sempre que possível, crie dois builds com o SDK 54 usando o mesmo código, as mesmas versões de dependências, a mesma configuração do Hermes e as mesmas configurações de build. Altere apenas a flag de arquitetura. Isso ajuda a separar os efeitos da arquitetura dos efeitos das atualizações de SDK e engine.
Se as dependências impedirem essa comparação, descreva o resultado como o efeito da atualização completa. Não atribua toda melhoria ao Fabric ou aos TurboModules.
Use builds de produção em dispositivos físicos para medir os tempos percebidos pelo usuário. O modo de desenvolvimento acrescenta sobrecarga que pode distorcer os resultados. Use sessões separadas de análise de desempenho para investigar as causas. Orientações de desempenho do React Native
Meça os resultados percebidos pelos usuários
Métrica | Defina um cenário reproduzível |
|---|---|
Inicialização a frio | Da abertura do processo até uma tela específica estar visível e utilizável |
Latência de interação | Do toque ou da tecla pressionada até a resposta visível |
Fluidez dos frames | Frames perdidos durante uma sequência fixa de rolagem ou navegação |
Memória | Memória do processo após a inicialização, após um fluxo pesado e depois de sair dele |
Latência de recurso nativo | Primeiro uso e usos posteriores de uma operação nativa representativa |
Confiabilidade | Falhas, ANRs do Android e regressões funcionais |
Use a análise de desempenho do React para investigar o trabalho dos componentes e ferramentas nativas de análise para investigar a atividade da CPU e o comportamento da plataforma. A duração do commit do React, sozinha, não mede o tempo até os pixels aparecerem na tela. O React Native documenta a análise nativa com Android Studio e Instruments. Guia de análise de desempenho
Mantenha consistentes o dispositivo, o sistema operacional, os dados, as condições de cache e o comportamento da rede. Alterne execuções das versões antiga e nova para reduzir o viés de ordem e deixe os dispositivos esfriarem entre testes exigentes. Comece com execuções repetidas e aumente o número de amostras se os resultados apresentarem muito ruído. Informe o tamanho da amostra, a mediana e a variabilidade; os percentis extremos precisam de observações suficientes para serem significativos.
Por fim, valide a disponibilização em produção. Uma inicialização mais rápida não é suficiente se a inicialização da câmera piorar ou a taxa de falhas aumentar. A migração é bem-sucedida quando o aplicativo funciona de forma confiável, oferece os recursos de que você precisa e as melhorias de desempenho alegadas se confirmam em uma comparação controlada.