Hermes — to silnik JavaScript z kompilacją AOT (Ahead-of-Time), opracowany przez Meta dla React Native i zoptymalizowany pod kątem urządzeń mobilnych z ograniczoną pamięcią. Według oficjalnego bloga Meta Engineering (2022), Hermes skraca czas uruchamiania aplikacji o 20-50% i zmniejsza rozmiar pakietu o 30-40% w porównaniu z JavaScriptCore. W przeciwieństwie do V8 czy JSC, Hermes nie używa kompilacji JIT na urządzeniu — cały JavaScript jest kompilowany do bajtkodu na etapie budowania przez Hermes CLI. Jest to szczególnie ważne dla iOS, gdzie kompilacja JIT jest ograniczona przez polityki App Store.
Najważniejsze
Hermes — to kompaktowy silnik JavaScript z otwartym kodem źródłowym (licencja MIT), stworzony przez Meta dla React Native i zoptymalizowany do pracy na urządzeniach mobilnych z ograniczonymi zasobami. Główną innowacją Hermes jest rezygnacja z kompilacji JIT na rzecz AOT (Ahead-of-Time). Na etapie budowania aplikacji React Native, Metro Bundler przekazuje skompilowany JavaScript do Hermes CLI, który przekształca go w bajtkod HBC (Hermes ByteCode). Ten bajtkod jest wykonywany bezpośrednio przez silnik bez dodatkowej kompilacji na urządzeniu. Takie podejście zapewnia przewidywalną wydajność: brak „rozgrzewania" JIT, brak pauz na kompilację, brak dodatkowego zużycia baterii. Hermes został zaprojektowany z uwzględnieniem ograniczeń urządzeń mobilnych: mała ilość RAM (1-4 GB), ograniczone zużycie energii i konieczność szybkiego zimnego startu. Pierwsze publiczne wydanie Hermes miało miejsce w 2019 roku, a od React Native 0.70 (2022) silnik stał się standardem na Androidzie.
JavaScriptCore (JSC) — standardowy silnik WebKit, używany przez Safari i React Native przed wersją 0.70. JSC obsługuje kompilację JIT, co zapewnia wysoką wydajność przy złożonych operacjach JavaScript. Jednak JIT wymaga rozgrzania: pierwsze sekundy działania kodu są wolniejsze (tryb interpretowany), następnie JIT kompiluje gorące fragmenty. Na iOS JIT jest praktycznie niedostępny ze względu na polityki App Store (zakaz dynamicznego generowania kodu), dlatego JSC na iOS działa tylko w trybie interpretowanym — wydajność spada. JSC ma większy rozmiar binarny (około 10 MB) i zużywa więcej RAM z powodu infrastruktury JIT. Hermes nie jest zależny od JIT, co zapewnia przewidywalną wydajność natychmiast po uruchomieniu (cold start). JSC pełniej obsługuje standard ECMAScript (w tym Proxy, BigInt, Reflect), ale kosztem większego zużycia zasobów. Dla projektów React Native, gdzie kluczowe są zimny start i mały rozmiar — Hermes jest preferowany. Dla projektów z ciężkimi obliczeniami w JS (gry, WebGL) — JSC może zapewnić wyższą wydajność szczytową.
| Parametr | Hermes | JavaScriptCore |
|---|---|---|
| Kompilacja | AOT (na etapie budowania) | JIT + Interpretowany (na urządzeniu) |
| Cold start | 20-50% szybciej | Poziom bazowy |
| Rozmiar pakietu | 30-40% mniejszy | Poziom bazowy |
| Zużycie RAM | 20-30% mniejsze | Poziom bazowy |
| ECMAScript | ES2020 (ograniczenia) | ES2022+ (pełny) |
| iOS JIT | Niewymagany | Niedostępny (tylko interpretowany) |
| Binarka | ~3 MB | ~10 MB |
Kompilacja AOT (Ahead-of-Time) w Hermes odbywa się w dwóch etapach. W pierwszym etapie Metro Bundler zbiera pliki JavaScript w jeden pakiet i przekazuje go do Hermes CLI (narzędzie hermesc). W drugim etapie hermesc parsuje JavaScript AST, generuje reprezentację pośrednią HIR (Hermes Intermediate Representation), a następnie emituje binarny bajtkod HBC. Wynikiem jest plik z rozszerzeniem .hbc, który zawiera tylko bajtkod bez oryginalnego JavaScriptu. Hermes runtime ładuje HBC bezpośrednio, bez parsowania i kompilacji. To radykalnie przyspiesza uruchamianie: zamiast parsowania tysięcy linii JS (async) silnik czyta wstępnie skompilowany format binarny. AOT również zmniejsza rozmiar: bajtkod jest średnio o 30% bardziej kompaktowy niż JavaScript AST. Wadą AOT jest brak możliwości wykonania eval, new Function czy dynamicznego require w czasie wykonania — wszystkie moduły muszą być znane na etapie budowania.
# Zainstaluj Hermes CLI samodzielnie
npm install hermes-engine
# Skompiluj JS do bajtkodu HBC
npx hermesc -emit-binary -out bundle.hbc bundle.js
# Statystyki bajtkodu
npx hermesc -dump-bytecode bundle.hbc # shows HBC instructions
# Rozmiar oryginalnego JS vs HBC
wc -c bundle.js # 2,300,000 bytes
wc -c bundle.hbc # 1,450,000 bytes (37% reduction)
Wydajność Hermes mierzona jest według trzech kluczowych metryk: Time-To-Interactive (TTI), rozmiar APK/IPA i zużycie RAM. Według Meta, na Androidzie Hermes skraca TTI o 34% w porównaniu z JSC: z 4,2 sekund do 2,8 sekund na urządzeniu średniej klasy (Moto G7). Rozmiar APK zmniejsza się o 28% dzięki kompaktowemu bajtkodowi i brakowi bibliotek JIT. Zużycie RAM jest średnio o 22% niższe przy tym samym obciążeniu — jest to szczególnie ważne dla urządzeń z 2-3 GB RAM. Na iOS zysk jest jeszcze większy: ponieważ JSC nie może używać JIT, Hermes daje wzrost TTI aż do 45%. Metryki oparte są na testach Meta z aplikacją Facebook Lite. W rzeczywistych projektach zysk jest różny: dla prostych ekranów (listy, tekst) Hermes daje większy wzrost, dla ciężkich animacji — mniejszy. Narzędzie profilowania: React Native Profiler + hermes profile --heap.
Hermes zawiera wbudowany profiler pamięci, dostępny przez Chrome DevTools. Podłącz się do aplikacji przez Metro, otwórz zakładkę Memory i wybierz Hermes (JavaScript) w typach snapshotów. Hermes obsługuje trzy typy snapshotów: Heap Snapshot (wszystkie obiekty), Allocation Timeline (czas życia obiektów) i Allocation Sampling (profil wybiórczy). Hades GC zmniejsza liczbę pauz GC do minimum — średnio jedna pauza trwająca 2-5ms na 10 sekund pracy wobec pauz 10-20ms u JSC w tym samym okresie.
Hades GC — to garbage collector w Hermes, zaprojektowany dla scenariuszy mobilnych z minimalnymi pauzami. W przeciwieństwie do mark-sweep GC w JSC, Hades używa współbieżnej (concurrent) kolekcji: kolektor działa równolegle z głównym wątkiem wykonawczym, zatrzymując go tylko na krótkie okresy. Hades GC dzieli stertę na pokolenia: młode pokolenie (nursery) jest zbierane często i szybko (Scavenge), stare pokolenie zbierane jest rzadziej i z mniejszymi pauzami. Rozmiar sterty jest konfigurowalny: domyślnie — 2/3 dostępnego RAM aplikacji, minimalny próg — 32 MB. Hades nie używa podejścia stop-the-world: nawet pełna kolekcja starego pokolenia trwa nie dłużej niż 5-8ms. Kolektor jest zoptymalizowany pod typowy scenariusz mobilny: dużo krótkożyjących obiektów (tymczasowe stringi, obiekty React fibre), mało długożyjących. Dla aplikacji z intensywnym tworzeniem obiektów (listy, animacje) Hades zapewnia płynniejszy FPS w porównaniu z JSC.
Włączenie Hermes zależy od wersji React Native i platformy. Od React Native 0.70 Hermes jest domyślnie włączony dla Androida w nowych projektach. Dla iOS Hermes jest opcjonalny. Aby włączyć na Androidzie: w pliku android/app/build.gradle ustaw enableHermes: true w bloku project.ext.react.enableHermes. Dla iOS: ustaw hermes_enabled na true w Podfile przez use_react_native!(:path => config[:reactNativePath], :hermes_enabled => true). Po włączeniu wykonaj pod install. Dla istniejących projektów sprawdź kompatybilność bibliotek: Hermes nie obsługuje Proxy, eval i niektórych funkcji ES2021. Użyj npx react-native info aby sprawdzić konfigurację. Aby przełączyć między Hermes a JSC, zmień flagę i wykonaj clean rebuild.
// android/app/build.gradle — włącz Hermes
project.ext.react = [
enableHermes: true,
cliPath: "node_modules/react-native/cli.js"
]
// iOS/Podfile — włącz Hermes na 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
Główne ograniczenia Hermes są związane z rezygnacją z JIT. Hermes nie obsługuje: Proxy i Reflect API (używane w MobX, Vue, niektórych state-menadżerach), BigInt (duże liczby), Symbol.toStringTag, WeakRef i FinalizationRegistry. Funkcje eval i new Function generują wyjątek podczas wykonania. Callbacki metod Array.prototype.flat i flatMap z argumentem this działają z ograniczeniami. Formaty dat ISO 8601 ze strefami czasowymi są obsługiwane nie w pełni. Większość tych ograniczeń nie wpływa na typowe aplikacje React Native: React i React Native używają ograniczonego zestawu funkcji ES. Jeśli biblioteka wymaga Proxy (np. MobX 6+ z Proxy), użyj configure({ useProxies: false }) lub wybierz alternatywę. Aby sprawdzić kompatybilność istniejącego kodu, uruchom npx hermesc -check na swoim pakiecie — pokaże on listę nieobsługiwanych funkcji.
Często zadawane pytania
W konsoli aplikacji wykonaj console.log(global.HermesInternal). Jeśli obiekt HermesInternal istnieje — aplikacja działa na Hermes. Alternatywnie: console.log(global.HermesInternal?.getRuntimeProperties()) — wyświetli wersję silnika i parametry GC. W kompilacjach Release HermesInternal może być niedostępny w celu minimalizacji rozmiaru.
Sprawdź ustawienia Podfile: Hermes na iOS wymaga New Architecture (Fabric Renderer). Ustaw :hermes_enabled => true, wykonaj pod install --repo-update. Jeśli projekt jest aktualizowany z React Native poniżej 0.70, sprawdź kompatybilność bibliotek z New Architecture. Wyłącz Hermes, jeśli biblioteka zewnętrzna wymaga JSC — w tym celu zmień flagę na false i reinstaluj Pods.
Nie, Hermes nie wpływa na Hot Reload / Fast Refresh. Podczas rozwoju Metro uruchamia pakiet JavaScript bez kompilacji Hermes (plain JS). Bajtkod Hermes jest tworzony tylko dla kompilacji Release. W trybie Debug używany jest standardowy JavaScriptCore lub Hermes w trybie interpretowanym. Szybkość Hot Reload nie zmienia się przy włączonym Hermes w konfiguracji — przełączanie następuje tylko na etapie budowania produkcyjnego.
Tak, od Expo SDK 45 Hermes jest obsługiwany dla managed workflow. W app.json ustaw "jsEngine": "hermes". Dla bare workflow Hermes działa jak w zwykłym projekcie React Native. Expo Go nie obsługuje Hermes — użyj Expo Dev Client lub EAS Build do kompilacji z Hermes. Sprawdź kompatybilność bibliotek przez expo doctor.
Użyj React Native Performance Monitor (metryki FPS) i Hermes Profiling Tools. Zbuduj dwie wersje aplikacji — z Hermes i JSC — na tym samym urządzeniu. Zmierz: cold start (od kliknięcia ikony do pierwszego interaktywnego ekranu), TTI (Time-To-Interactive), rozmiar APK/IPA i szczytowe zużycie RAM. Uruchom testy co najmniej 3 razy dla każdej konfiguracji. Typowa przewaga Hermes: start o 20-40% szybszy, RAM o 15-25% mniejszy.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również