Hermes è un motore JavaScript con compilazione AOT (Ahead-of-Time), sviluppato da Meta per React Native e ottimizzato per dispositivi mobili con memoria limitata. Secondo il blog ufficiale di Meta Engineering (2022), Hermes riduce il tempo di avvio dell'app del 20-50% e diminuisce la dimensione del bundle del 30-40% rispetto a JavaScriptCore. A differenza di V8 o JSC, Hermes non utilizza la compilazione JIT sul dispositivo — tutto il JavaScript viene compilato in bytecode durante la fase di build tramite Hermes CLI. Questo è particolarmente importante per iOS, dove la compilazione JIT è limitata dalle politiche dell'App Store.
Punti chiave
Hermes è un motore JavaScript compatto open source (licenza MIT) creato da Meta per React Native e ottimizzato per funzionare su dispositivi mobili con risorse limitate. L'innovazione principale di Hermes è l'abbandono della compilazione JIT in favore di AOT (Ahead-of-Time). Durante la fase di build dell'app React Native, Metro Bundler passa il JavaScript compilato a Hermes CLI, che lo trasforma in bytecode HBC (Hermes ByteCode). Questo bytecode viene eseguito direttamente dal motore senza compilazione aggiuntiva sul dispositivo. Questo approccio fornisce prestazioni prevedibili: nessun riscaldamento JIT, nessuna pausa di compilazione, nessun consumo extra della batteria. Hermes è progettato tenendo conto dei limiti dei dispositivi mobili: poca RAM (1-4 GB), consumo energetico limitato e necessità di un avvio a freddo rapido. La prima versione pubblica di Hermes è stata nel 2019 e, a partire da React Native 0.70 (2022), il motore è diventato lo standard su Android.
JavaScriptCore (JSC) è il motore WebKit standard utilizzato da Safari e React Native prima della versione 0.70. JSC supporta la compilazione JIT, che offre prestazioni elevate per operazioni JavaScript complesse. Tuttavia, JIT richiede riscaldamento: i primi secondi di esecuzione sono più lenti (modalità interpretata), poi JIT compila i percorsi caldi. Su iOS, JIT è praticamente indisponibile a causa delle politiche dell'App Store (divieto di generazione dinamica di codice), quindi JSC su iOS funziona solo in modalità interpretata — le prestazioni calano. JSC ha una dimensione binaria maggiore (circa 10 MB) e consuma più RAM a causa dell'infrastruttura JIT. Hermes non dipende da JIT, fornendo prestazioni prevedibili immediatamente dopo l'avvio (cold start). JSC supporta lo standard ECMAScript in modo più completo (inclusi Proxy, BigInt, Reflect), ma a costo di un maggiore consumo di risorse. Per i progetti React Native dove l'avvio a freddo e le dimensioni ridotte sono critici — Hermes è preferibile. Per progetti con calcoli JS pesanti (giochi, WebGL) — JSC può offrire prestazioni di picco più elevate.
| Parametro | Hermes | JavaScriptCore |
|---|---|---|
| Compilazione | AOT (al build) | JIT + Interpretato (sul dispositivo) |
| Cold start | 20-50% più veloce | Livello base |
| Dimensione bundle | 30-40% più piccola | Livello base |
| Consumo RAM | 20-30% in meno | Livello base |
| ECMAScript | ES2020 (limitazioni) | ES2022+ (completo) |
| JIT iOS | Non richiesto | Non disponibile (solo interpretato) |
| Binario | ~3 MB | ~10 MB |
La compilazione AOT (Ahead-of-Time) in Hermes avviene in due fasi. Nella prima fase, Metro Bundler raccoglie i file JavaScript in un unico bundle e lo passa a Hermes CLI (l'utilità hermesc). Nella seconda fase, hermesc analizza l'AST JavaScript, genera una rappresentazione intermedia HIR (Hermes Intermediate Representation) e quindi emette bytecode HBC binario. Il risultato è un file .hbc che contiene solo bytecode senza il JavaScript originale. Il runtime di Hermes carica HBC direttamente, senza analisi e compilazione. Questo accelera radicalmente l'avvio: invece di analizzare migliaia di righe di JS (async), il motore legge un formato binario precompilato. AOT riduce anche le dimensioni: il bytecode è in media il 30% più compatto dell'AST JavaScript. Lo svantaggio di AOT — l'impossibilità di eseguire eval, new Function o require dinamico in fase di esecuzione — tutti i moduli devono essere noti al momento del build.
# Installare Hermes CLI standalone
npm install hermes-engine
# Compilare JS in bytecode HBC
npx hermesc -emit-binary -out bundle.hbc bundle.js
# Statistiche bytecode
npx hermesc -dump-bytecode bundle.hbc # shows HBC instructions
# Dimensione JS originale vs HBC
wc -c bundle.js # 2,300,000 bytes
wc -c bundle.hbc # 1,450,000 bytes (37% reduction)
Le prestazioni di Hermes sono misurate da tre metriche chiave: Time-To-Interactive (TTI), dimensione APK/IPA e consumo di RAM. Secondo i dati di Meta, su Android Hermes riduce il TTI del 34% rispetto a JSC: da 4,2 secondi a 2,8 secondi su un dispositivo di fascia media (Moto G7). La dimensione dell'APK diminuisce del 28% grazie al bytecode compatto e all'assenza di librerie JIT. Il consumo di RAM è in media inferiore del 22% a parità di carico — questo è particolarmente importante per i dispositivi con 2-3 GB di RAM. Su iOS, il vantaggio è ancora più significativo: poiché JSC non può utilizzare JIT, Hermes fornisce un miglioramento del TTI fino al 45%. Le metriche si basano su test di Meta con l'app Facebook Lite. Nei progetti reali, il vantaggio varia: per schermate semplici (liste, testo) Hermes offre un miglioramento maggiore, per animazioni pesanti — minore. Strumento di profilazione: React Native Profiler + hermes profile --heap.
Hermes include un profilatore di memoria integrato accessibile tramite Chrome DevTools. Connettiti all'app tramite Metro, apri la scheda Memoria e seleziona Hermes (JavaScript) nei tipi di snapshot. Hermes supporta tre tipi di snapshot: Heap Snapshot (tutti gli oggetti), Allocation Timeline (durata degli oggetti) e Allocation Sampling (profilo a campione). Hades GC riduce le pause del GC al minimo — in media una pausa di 2-5ms ogni 10 secondi di funzionamento, contro pause di 10-20ms per JSC nello stesso periodo.
Hades GC è il garbage collector di Hermes, progettato per scenari mobili con pause minime. A differenza del GC mark-sweep in JSC, Hades utilizza la raccolta concorrente: il collector lavora in parallelo con il thread di esecuzione principale, fermandolo solo per brevi intervalli. Hades GC divide l'heap in generazioni: la generazione giovane (nursery) viene raccolta frequentemente e rapidamente (Scavenge), la generazione vecchia viene raccolta meno spesso e con pause più brevi. La dimensione dell'heap è configurabile: per impostazione predefinita — 2/3 della RAM disponibile dell'app, soglia minima — 32 MB. Hades non utilizza l'approccio stop-the-world: anche una raccolta completa della generazione vecchia non richiede più di 5-8ms. Il collector è ottimizzato per scenari mobili tipici: molti oggetti a vita breve (stringhe temporanee, oggetti React fiber), pochi a vita lunga. Per applicazioni con creazione intensiva di oggetti (liste, animazioni), Hades fornisce FPS più fluidi rispetto a JSC.
L'abilitazione di Hermes dipende dalla versione di React Native e dalla piattaforma. A partire da React Native 0.70, Hermes è abilitato per impostazione predefinita per Android nei nuovi progetti. Per iOS, Hermes è opzionale. Per abilitare su Android: nel file android/app/build.gradle imposta enableHermes: true nel blocco project.ext.react.enableHermes. Per iOS: imposta hermes_enabled su true nel Podfile tramite use_react_native!(:path => config[:reactNativePath], :hermes_enabled => true). Dopo l'abilitazione, esegui pod install. Per progetti esistenti, verifica la compatibilità delle librerie: Hermes non supporta Proxy, eval e alcune funzionalità ES2021. Usa npx react-native info per verificare la configurazione. Per passare da Hermes a JSC, modifica il flag ed esegui una ricostruzione pulita.
// android/app/build.gradle — abilitare Hermes
project.ext.react = [
enableHermes: true,
cliPath: "node_modules/react-native/cli.js"
]
// iOS/Podfile — abilitare Hermes su iOS
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
target 'MyApp' do
config = use_native_modules!
use_react_native!(
:path => config[:reactNativePath],
:hermes_enabled => true
)
end
Le principali limitazioni di Hermes sono legate all'assenza di JIT. Hermes non supporta: Proxy e Reflect API (usati in MobX, Vue, alcuni state manager), BigInt (numeri grandi), Symbol.toStringTag, WeakRef e FinalizationRegistry. Le funzioni eval e new Function generano un'eccezione in fase di esecuzione. I callback dei metodi Array.prototype.flat e flatMap con un argomento this funzionano con limitazioni. I formati di data ISO 8601 con fusi orari non vengono elaborati completamente. La maggior parte di queste limitazioni non influisce sulle applicazioni React Native tipiche: React e React Native utilizzano un insieme limitato di funzionalità ES. Se una libreria richiede Proxy (ad esempio, MobX 6+ con Proxy), usa configure({ useProxies: false }) o scegli un'alternativa. Per verificare la compatibilità del codice esistente, esegui npx hermesc -check sul tuo bundle — mostrerà l'elenco delle funzionalità non supportate.
Domande frequenti
Nella console dell'app, esegui console.log(global.HermesInternal). Se l'oggetto HermesInternal esiste — l'app funziona su Hermes. In alternativa: console.log(global.HermesInternal?.getRuntimeProperties()) — mostrerà la versione del motore e i parametri GC. Nelle build Release, HermesInternal potrebbe non essere disponibile per ridurre le dimensioni.
Controlla le impostazioni del Podfile: Hermes su iOS richiede New Architecture (Fabric Renderer). Imposta :hermes_enabled => true, esegui pod install --repo-update. Se il progetto viene aggiornato da React Native inferiore a 0.70, verifica la compatibilità delle librerie con New Architecture. Disabilita Hermes se una libreria di terze parti richiede JSC — imposta il flag su false e reinstalla i Pods.
No, Hermes non influisce su Hot Reload / Fast Refresh. Durante lo sviluppo, Metro esegue il bundle JavaScript senza compilazione Hermes (JS puro). Il bytecode Hermes viene compilato solo per le build Release. In modalità Debug, viene utilizzato JavaScriptCore standard o Hermes in modalità interpretata. La velocità di Hot Reload non cambia quando Hermes è abilitato nella configurazione — il passaggio avviene solo nella fase di build di produzione.
Sì, a partire da Expo SDK 45, Hermes è supportato per managed workflow. In app.json specifica "jsEngine": "hermes". Per bare workflow, Hermes funziona come in un normale progetto React Native. Expo Go non supporta Hermes — usa Expo Dev Client o EAS Build per build con Hermes. Verifica la compatibilità delle librerie tramite expo doctor.
Usa React Native Performance Monitor (metriche FPS) e Hermes Profiling Tools. Crea due versioni dell'app — con Hermes e JSC — sullo stesso dispositivo. Misura: cold start (dal tocco sull'icona al primo schermo interattivo), TTI (Time-To-Interactive), dimensione APK/IPA e consumo massimo di RAM. Esegui i test almeno 3 volte per ogni configurazione. Vantaggio tipico di Hermes: avvio 20-40% più veloce, RAM 15-25% inferiore.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche