React Native: Uma análise da nova arquitetura de Turbo Native Modules
DEV Community

React Native: Uma análise da nova arquitetura de Turbo Native Modules

A partir da versão 0.76 do react native a nova arquitetura foi habilitada por padrão para aplicativos a nível de produção.Essa nova arquitetura agora permite a criação de modulos nativos A nova arquitetura introduz TurboModules + Codegen, que mudam completamente a forma como JavaScript conversa com código nativo. Neste artigo, você vai aprender sobre: - Problemas da arquitetura antiga - Funcionamento da nova arquitetura - Um exemplo completo (Android + TypeScript) - Boas práticas no uso de Turbo Modules Problemas da arquitetura antiga Na arquitetura antiga o fluxo de comunicação dos módulos nativos era baseado na “Bridge”. Como o próprio nome remete seria a ponte entre o JavaScript que é executado na Thread até a entrega de elementos nativos com o objetivo de gerar interfaces interativas com usabilidade e experiencia de desenvolvimento o máximo possível semelhantes ao desenvolvimento nativo utilizando uma unica code-base para isso. Para facilitar seu entendimento pense os dois sistemas são dois estrangeiros um japonês e outro espanhol, que precisam conversar entre si e para isso buscam um idioma comum entre os dois para se comunicarem e usam o inglês por exemplo, nesse caso da arquitetura antiga do react native o “idioma” em comum entre seria o JSON. Isso funcionava bem quando os aplicativos eram mais simples, mas a medida que os aplicativos começaram a crescer com mais funcionalidades, aumento de complexidade, evolução do react começou a surgir alguns problemas nessa arquitetura: 1) A Bridge é apenas async Por ser somente assíncrona, ter serialização JSON em cada chamada, lentidão com o aumento de grandes transferências de dados e além da possibilidade de ocorrer um JSON overhead em determinados casos. 2) Renderização de UI O motor de renderização usado era o UIManger (Native) o que fazia as atualizações de interfaces causar lag além da latência alta em determinadas animações. 3) Difícil suporte as novas features no React Renderização simultânea, Suspense e Transitions e possíveis novos recursos do React no futuro eram bloqueados de serem suportados de forma limpa por usar essa arquitetura. Era necessário uma estrutura mais forte para estruturar esse novos recursos, nessa situação entra a necessidade da criação da nova arquitetura do react native. 4) Módulos nativos mais pesados - Todos os módulos carregados no startup da aplicação. - Comunicação lenta e sem type safety real - Contratos verificados em runtime - Sem suporte C++ cross-platform - Cada chamada passa por uma camada intermediária Funcionamento da nova arquitetura Na nova arquitetura funciona dessa forma; A nova arquitetura é construída como parte de uma reformulação completa nos módulos e nas camadas que fazem a ligação entre o código Javascript e o código nativo. Essa nova arquitetura têm como diferenciais quatro partes principais: - O novo sistema de módulos nativos - O novo renderizador - O Event Loop - Remoção da Bridge Essa nova arquitetura traz como principais benefícios a questão performática, menor tamanho fazendo os módulos carregarem sob demanda, uma melhor experiencia de UX, e uma melhor arquitetura preparada para escalar conforme sua aplicação cresce. O principio de funcionamento da nova arquitetura é basicamente assim: 1) JSI (JavaScript Interface) É uma camada em C++ que faz a comunicação diretamente o runtime do JavaScript (Hermes, JSC) com o código nativo. essa comunicação é feita direta na memória sem serialização (JSON) e sem filas de mensagens. Tendo como resultado essa comunicação sendo síncrona ou assíncrona e feita de forma muito mais rápida que antes 2) Fabric Um novo motor de renderização que substitui o antigo UIManager. Ele formado por três conceitos: Shadow Tree (UI virtual) → Diff (Calculo de mudanças)→ Mount (aplica mudanças na UI nativa ). Com isso tivemos uma melhora considerável a consistência e o desempenho da UI 3) Turbo Modules substituto para os módulos nativos legados (NativeModules), permitindo uma comunicação mais rápida e segura entre o JavaScript e código nativo. E têm como características e diferenciais os seguintes aspectos: - Lazy loading: carrega só quando usado - Chamadas diretas via JSI - Baixa latência, sincronismo possível - Inicialização mais rápida - Menos consumo de memória 4) Codegen Ele gera automaticamente código nativo para TurboModules e os componentes, a partir de uma spec em TypeScript. Garantindo segurança de tipo de ponta a ponta e evitando erros manuais. Como criar um módulo Turbo Native Vou fornecer o guia passo a passo de como você pode para criar seu primeiro Turbo Module nativo com a nova arquitetura do do React Native. Nesse exemplo vou criar de modo simples e genérico um modulo nativo, no qual com você pode usar esses conceitos em qualquer outra funcionalidade nativa. Pré-requisitos - React Native 0.76+ (New Architecture habilitada por padrão) - Xcode 15+ - Android Studio com Kotlin - Node.js 18+ 1: Estrutura das pastas Inicie um projeto react native A estrutural final das pastas para a criação de um módulo nativo vai ficar assim: project-root/ ├── src/ │ ├── native/ │ │ ├── NativeBatteryToast.ts # Codegen Spec (contrato TS ↔️ Nativo) │ │ └── BatteryToast.ts # Wrapper tipado para consumo em componentes │ └── screens/ │ └── battery.tsx # Tela demo ├── android/ │ └── app/src/main/java/com/myprojectname/ │ ├── MainApplication.kt # Registro do package │ └── batterytoast/ │ ├── BatteryToastModule.kt # Implementação nativa Android │ └── BatteryToastPackage.kt # Registro do módulo no RN └── ios/ └── MyProjectName/ └── BatteryToast/ └── BatteryToastModule.mm # Implementação nativa iOS (Objective-C++) 2: Criar o Codegen Spec (TypeScript) O Codegen Spec é o contrato entre JavaScript e o código nativo. Ele define quais métodos existem, seus parâmetros e seus retornos. Regras do Codegen Spec: - O nome do arquivo Deve começar com Native (ex:NativeMeuModulo.ts ) - A interface Deve se chamar Spec e estenderTurboModule - Tipos suportados: string ,number ,boolean ,Object (com shape definida),Array ,Promise - O nome passado para getEnforcing() é o identificador do módulo nativo Crie o arquivo src/native/NativeCustomDeviceInfo.ts : import type { TurboModule } from 'react-native'; import { TurboModuleRegistry } from 'react-native'; export interface Spec extends TurboModule { // Métodos síncronos - retornam imediatamente via JSI getDeviceModel(): string; getSystemName(): string; getSystemVersion(): string; // Métodos assíncronos - retornam Promise getDeviceInfo(): Promise ; // Método com parâmetros isFeatureSupported(featureName: string): Promise ; // Obrigatórios para módulos com eventos addListener(eventName: string): void; removeListeners(count: number): void; } export default TurboModuleRegistry.getEnforcing ('CustomDeviceInfo'); 3: Configurar o Codegen no package.json Adicione a seção codegenConfig ao package.json : { "codegenConfig": { "name": "AppSpecs", "type": "modules", "jsSrcsDir": "src/native", "android": { "javaPackageName": "com.seuapp" }, "ios": { "modulesProvider": { "CustomDeviceInfo": "DeviceInfoModule" } } } } - name : Nome do módulo gerado (usado nos imports nativos) - jsSrcsDir : Diretório onde estão os specs - modulesProvider (iOS): Mapeia o nome do módulo JS para a classe nativa 4: Implementação Android (Kotlin) 4.1 Criar o Módulo Crie android/app/src/main/java/com/seuapp/deviceinfo/DeviceInfoModule.kt : package com.seuapp import android.os.Build import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.WritableNativeMap import com.facebook.react.module.annotations.ReactModule import com.nativemodule.NativeCustomDeviceInfoSpec @ReactModule(name = DeviceInfoModule.NAME) class DeviceInfoModule(reactContext: ReactApplicationContext) : NativeCustomDeviceInfoSpec(reactContext) { companion object { const val NAME = "CustomDeviceInfo" } override fun getName(): String = NAME override fun getDeviceModel(): String { return "${Build.MANUFACTURER} ${Build.MODEL}" } override fun getSystemName(): String { return "Android" } override fun getSystemVersion(): String { return Build.VERSION.RELEASE } override fun getDeviceInfo(promise: Promise) { try { val packageInfo = reactApplicationContext.packageManager .getPackageInfo(reactApplicationContext.packageName, 0) val result = WritableNativeMap().apply { putString("model", getDeviceModel()) putString("systemName", getSystemName()) putString("systemVersion", getSystemVersion()) putString("appVersion", packageInfo.versionName ?: "unknown") putString("buildNumber", packageInfo.versionCode.toString()) } promise.resolve(result) } catch (e: Exception) { promise.reject("ERROR", "Failed to get device info: ${e.message}") } } override fun isFeatureSupported(featureName: String, promise: Promise) { val supported = when (featureName) { "bluetooth" -> reactApplicationContext.packageManager .hasSystemFeature("android.hardware.bluetooth") "camera" -> reactApplicationContext.packageManager .hasSystemFeature("android.hardware.camera") "nfc" -> reactApplicationContext.packageManager .hasSystemFeature("android.hardware.nfc") else -> false } promise.resolve(supported) } override fun addListener(eventName: String) { // No-op: required by EventEmitter interface } override fun removeListeners(count: Double) { // No-op: required by EventEmitter interface } } 4.2 Criar o Package package com.seuapp import com.facebook.react.TurboReactPackage import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.module.model.ReactModuleInfo import com.facebook.react.module.model.ReactModuleInfoProvider class DeviceInfoPackage : TurboReactPackage() { override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { return when (name) { DeviceInfoModule.NAME -> DeviceInfoModule(reactContext) else -> null } } override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { return ReactModuleInfoProvider { mapOf( Devic

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.