Metro — bundler JavaScript zaprojektowany specjalnie dla React Native i używany domyślnie do budowania aplikacji mobilnych na tej platformie. W przeciwieństwie do uniwersalnych bundlerów, Metro jest zoptymalizowany pod ograniczone zasoby urządzeń mobilnych i zapewnia integrację z Hermes engine oraz Fast Refresh. Według Metro Documentation (2026), bundler obsługuje ponad 90% projektów React Native na świecie.
Najważniejsze
Metro to bundler JavaScript z otwartym kodem źródłowym, stworzony przez zespół Meta dla React Native. Zastąpił Packagera z wcześniejszych wersji React Native i stał się standardowym narzędziem kompilacji dla tej platformy. Metro nie jest przeznaczony do tworzenia stron internetowych: jego architektura jest zorientowana wyłącznie na aplikacje mobilne działające pod kontrolą silników JavaScript JSC (JavaScriptCore) lub Hermes.
Metro pojawił się w 2018 roku jako zastĉpca starego React Native Packager. Głównym powodem powstania była potrzeba bundlera, który efektywnie współpracuje z milionami modułów w dużych aplikacjach mobilnych. W przeciwieństwie do Webpack, Metro nie obsługuje CSS, HTML ani obrazów jako modułów — te zasoby są przetwarzane przez osobne narzędzia React Native. Facebook używa Metro do budowania swojej głównej aplikacji, która zawiera ponad 100 tysięcy plików.
Metro jest rozpowszechniany przez npm jako pakiet metro i instaluje się automatycznie wraz z React Native CLI. Wersja Metro jest powiązana z wersją React Native, dlatego aktualizacja bundlera następuje wraz z aktualizacją platformy.
Metro przyjmuje na wejściu punkt wejścia aplikacji, buduje graf zależności, transformuje każdy moduł i serializuje wynik w jeden bundle. Proces przechodzi trzy fazy: Resolve (rozpoznawanie modułów), Transform (transformacja) i Serialize (serializacja).
// Przykład konfiguracji Metro dla niestandardowej transformacji
const metroConfig = {
transformer: {
babelTransformerPath: require('metro-babel-transformer'),
async transform({ src, filename, options }) {
const result = await babelTransform({ src, filename, options });
return {
ast: result.ast,
code: result.code,
map: result.map,
dependencies: [],
};
},
},
serializer: {
createModuleIdFactory() {
let nextId = 0;
const moduleIds = new Map();
return ({ path }) => {
if (!moduleIds.has(path)) {
moduleIds.set(path, nextId++);
}
return moduleIds.get(path);
};
},
},
};
W fazie Resolve Metro określa lokalizację każdego importowanego modułu, uwzględniając aliasy i node_modules. W fazie Transform każdy plik przechodzi przez Babel-transformer do konwersji JSX, TypeScript i innych rozszerzeń na czysty JavaScript. Finałowa faza Serialize zbiera wszystkie przetworzone moduły w jeden lub kilka plików bundle.
Metro jest zbudowany na modularnej architekturze, gdzie każdy etap kompilacji jest zaimplementowany jako osobny komponent. Pozwala to na zastępowanie standardowych modułów niestandardowymi bez zmiany rdzenia bundlera. Architektura Metro obejmuje trzy główne pakiety: metro, metro-config i metro-resolver.
Metro Server — serwer deweloperski, który uruchamia się komendą npx react-native start. Obsługuje on żądania transformacji modułów w czasie rzeczywistym, zapewniając Fast Refresh. Module Store — pamięć podręczna przechowująca przetworzone moduły w pamięci w celu przyspieszenia kolejnych kompilacji. Dependency Graph — graf zależności, który jest aktualizowany przyrostowo przy zmianach plików.
Watchman — obserwator plików od Meta, którego Metro używa do wykrywania zmian w systemie plików. Bez Watchmana Metro musiałby skanować całą strukturę plików przy każdej zmianie, co znacznie spowalnia rozwój. Instalacja Watchmana jest obowiązkowa do komfortowej pracy z Metro w projektach dowolnej wielkości.
Konfiguracja Metro jest określana w pliku metro.config.js w katalogu głównym projektu. Typowy plik definiuje dodatkowe foldery do wyszukiwania modułów, blokady do wykluczania niepotrzebnych plików i niestandardowe trans-formery. W React Native 0.72+ używane jest automatyczne wykrywanie konfiguracji, ale dla monorepozytoriów konfiguracja jest obowiązkowa.
// metro.config.js
const config = {
resolver: {
sourceExts: ['js', 'jsx', 'ts', 'tsx', 'json'],
nodeModulesPaths: ['node_modules'],
blockList: [/\.test\.js$/, /__tests__\/.*/],
extraNodeModules: {
'shared-components': path.resolve(__dirname, '../shared/src'),
},
},
transformer: {
minifierConfig: {
keep_classnames: true,
keep_fnames: true,
mangle: { reserved: ['React', 'Component'] },
},
},
};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
blockList wyklucza pliki testowe z kompilacji, zmniejszając rozmiar bundle. nodeModulesPaths wskazuje dodatkowe ścieżki do wyszukiwania pakietów — kluczowe dla monorepozytoriów. extraNodeModules tworzy aliasy dla wspólnych pakietów używanych w różnych projektach monorepozytorium.
Metro udostępnia kilka mechanizmów optymalizacji rozmiaru bundle i szybkości kompilacji. Inline Requires — najbardziej efektywna technika, zamieniająca importy na najwyższym poziomie w lokalne require wewnątrz funkcji. RAM bundles pozwalają na przyrostowe ładowanie modułów. Hermes kompiluje JavaScript do kodu bajtowego, zmniejszając rozmiar aplikacji o 20–30%.
// metro.config.js — optymalizacja dla produkcji
const config = {
transformer: {
async transform({ src, filename, options }) {
const inlineRequires = options.dev ? false : true;
return await defaultTransform({ src, filename, options: { ...options, inlineRequires } });
},
},
serializer: {
polyfillModuleNames: [],
},
};
// Aby włączyć Hermes — w build.gradle (Android):
// project.ext.react = [enableHermes: true, bundleInRelease: true]
// Dla iOS — w Podfile: :hermes_enabled => true
Do diagnostyki rozmiaru bundle Metro udostępnia flagę --bundle-output z wyjściem statystyk. Użyj bundle-visualizer do analizy składu bundle i znajdowania dużych modułów, które można ładować leniwie. Regularne sprawdzanie rozmiaru bundle powinno być częścią potoku CI/CD projektu React Native.
Metro obsługuje delta-bundle — mechanizm, w którym po pierwszej pełnej kompilacji serwer wysyła tylko zmiany (delta) między starą a nową wersją bundle. To radykalnie przyspiesza kolejne kompilacje: czas aktualizacji skraca się z sekund do dziesiąt-ków milisekund. Delta-bundle są szczególnie skuteczne na etapie rozwoju, gdy programista często zapisuje zmiany i przeładowuje aplikację.
Do włączenia delta-bundle w React Native używa się flagi --delta w komendzie npx react-native bundle. Po stronie klienta delta-bundle są obsługiwane od React Native 0.64. W kompilacjach produkcyjnych delta-bundle nie są stosowane — zamiast nich używany jest pełny bundle z kodem bajtowym Hermesa dla maksymalnej wydajności uruchamiania. Osiąga się to dzięki temu, że wzrost szybkości rozwoju kompensuje koszty pierwszej pełnej kompilacji.
Hermes — silnik JavaScript opracowany przez Meta specjalnie dla React Native. Metro generuje kod bajtowy Hermesa na etapie kompilacji, co pozwala aplikacji uruchamiać się bez kosztownej kompilacji JavaScript na urządzeniu. Do włączenia Hermesa wystarczy dodać enableHermes: true w metro.config.js i skonfigurować build.gradle lub Podfile. Hermes zmniejsza czas uruchamiania aplikacji o 30–50% i zmniejsza rozmiar APK o 20–30% w porównaniu z JavaScriptCore.
Przy używaniu Hermesa należy pamiętać o ograniczeniach: silnik nie obsługuje Proxy, Reflect i niektórych możliwości ES6. Większość aplikacji React Native nie używa tych możliwości bezpośrednio, ale niektóre biblioteki mogą powodować konflikty. Przed włączeniem Hermesa sprawdź kompatybilność wszystkich zależności projektu przez oficjalną listę kontrolną zgodności. Metro automatycznie przełącza się w tryb generowania kodu bajtowego po aktywacji Hermesa.
Do śledzenia efektywności kompilacji Metro używaj wbudowanych metryk dostępnych przez Flipper — narzędzie debugowania React Native. Metro publikuje zdarzenia: bundle_request, transform, resolve z czasem wykonania każdego etapu. Analiza tych danych pomaga zidentyfikować wąskie gardła: jeśli faza transform zajmuje ponad 70% czasu, problem leży w Babel-transformerze. Włącz prowadzenie logów Metro przez flagę --verbose do szczegółowej diagnostyki.
Często zadawane pytania
Technicznie tak — istnieją eksperymentalne projekty takie jak react-native-webpack, ale nie są oficjalnie wspierane. Metro jest zintegrowany z React Native bridge, Turbo Modules i Hermes na poziomie niedostępnym dla Webpacka. Zastąpienie Metro spowoduje utratę Fast Refresh i oficjalnego wsparcia.
React Native nie używa CSS do stylizacji — zamiast tego stosowana jest stylizacja JavaScript przez StyleSheet.create. Ponieważ Metro został stworzony wyłącznie dla React Native, obsługa CSS nie jest potrzebna. Do renderowania internetowego przez React Native Web CSS jest przetwarzany przez osobne narzędzia poza Metro.
Metro nie przetwarza obrazów ani czcionek jako modułów. Zasoby są podłączane przez require('./image.png'), ale Metro tylko rejestruje je jako zależności ciągowe. Rzeczywiste ładowanie obrazów jest wykonywane przez natywny kod React Native przez komponent Image, a Metro kopiuje zasoby do bundle.
Rollup generuje moduły ES z tree shaking na poziomie modułów, co daje minimalny rozmiar bundle dla bibliotek. Metro generuje bundle CommonJS z transformacją inline i optymalizacją pod silniki mobilne. Rollup nie obsługuje Fast Refresh i nie może współpracować z natywnymi modułami React Native.
Zimna kompilacja Metro jest spowalniana przez pełne skanowanie node_modules. Rozwiązania: dodaj watchFolders dla monorepozytoriów, użyj maxWorkers do równoległej transformacji i zainstaluj Watchman. Dla projektów z 10 000+ plików rozważ metro-memory-fs do buforowania w pamięci.
Podsumowanie
watchFolders i extraNodeModules do prawidłowego rozpoznawania modułów.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ż