Metro — JavaScript бъндлър, разработен специално за React Native и използван по подразбиране за събиране на мобилни приложения на тази платформа. За разлика от универсалните бъндлъри, Metro е оптимизиран за ограничените ресурси на мобилните устройства и осигурява интеграция с Hermes engine и Fast Refresh. Според Metro Documentation (2026), бъндлърът обработва над 90% от React Native проектите в света.
Основни поенти
Metro е JavaScript бъндлър с отворен код, създаден от екипа на Meta за React Native. Той замени Packager-а от ранните версии на React Native и стана стандартният инструмент за компилиране на тази платформа. Metro не е предназначен за уеб разработка: неговата архитектура е насочена изключително към мобилни приложения, работещи под JSC (JavaScriptCore) или Hermes JavaScript двигатели.
Metro се появява през 2018 г. като заместител на стария React Native Packager. Основната причина за създаването бе нуждата от бъндлър, който ефективно работи с милиони модули в големи мобилни приложения. За разлика от Webpack, Metro не поддържа CSS, HTML или изображения като модули — тези ресурси се обработват от отделни React Native инструменти. Facebook използва Metro за събиране на основното си приложение, което съдържа над 100 хиляди файла.
Metro се разпространява чрез npm като пакет metro и се инсталира автоматично заедно с React Native CLI. Версията на Metro е свързана с версията на React Native, така че актуализирането на бъндлъра става заедно с актуализирането на платформата.
Metro получава на вход входната точка на приложението, изгражда графа на зависимостите, трансформира всеки модул и сериализира резултата в единен бъндъл. Процесът преминава през три фази: Resolve (решаване на модули), Transform (трансформация) и Serialize (сериализация).
// Пример за конфигурация на Metro за персонализирана трансформация
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);
};
},
},
};
В фаза Resolve Metro определя местоположението на всеки импортиран модул, като взема предвид алиасите и node_modules. В фаза Transform всеки файл преминава през Babel трансформер за преобразуване на JSX, TypeScript и други разширения в чист JavaScript. Крайната фаза Serialize събира всички трансформирани модули в един или повече бъндъл файла.
Metro е изграден на модулна архитектура, където всяка фаза на компилацията е реализирана като отделен компонент. Това позволява замяна на стандартните модули с персонализирани без промяна на ядрото на бъндлъра. Архитектурата на Metro включва три основни пакета: metro, metro-config и metro-resolver.
Metro Server — разработен сървър, който се стартира с командата npx react-native start. Той обслужва заявки за трансформация на модули в реално време, осигурявайки Fast Refresh. Module Store — кеш, който съхранява трансформираните модули в паметта за ускоряване на повторните компилации. Dependency Graph — граф на зависимостите, който се актуализира инкрементално при промяна на файловете.
Watchman — наблюдател на файлове от Meta, който Metro използва за откриване на промени в файловата система. Без Watchman, Metro трябва да препроверява цялата файлова структура при всяка промяна, което значително забавява разработката. Инсталацията на Watchman е задължителна за комфортна работа с Metro на проекти от всякакви големина.
Настройката на Metro се определя в файла metro.config.js в корена на проекта. Типичен файл дефинира допълнителни папки за търсене на модули, блокировки за изключване на ненужни файлове и персонализирани трансформери. В React Native 0.72+ се използва автоматично откриване на конфигурацията, но за монорепозиториумите настройката е задължителна.
// 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 изключва тестовите файлове от компилацията, намалявайки размера на бъндъла. nodeModulesPaths посочва допълнителни пътища за търсене на пакети — критично за монорепозиториумите. extraNodeModules създава алиаси за общи пакети, използвани в различни проекти на монорепозиториума.
Metro предоставя няколко механизма за оптимизиране на размера на бъндъла и скоростта на компилацията. Inline Requires — най-ефективната техника, която превърща импортирането на най-високо ниво в локални require в функциите. RAM bundles позволяват инкрементално зареждане на модули. Hermes компилира JavaScript в байткод, намалявайки размера на приложението с 20–30%.
// metro.config.js — оптимизиране за производство
const config = {
transformer: {
async transform({ src, filename, options }) {
const inlineRequires = options.dev ? false : true;
return await defaultTransform({ src, filename, options: { ...options, inlineRequires } });
},
},
serializer: {
polyfillModuleNames: [],
},
};
// За включване на Hermes — в build.gradle (Android):
// project.ext.react = [enableHermes: true, bundleInRelease: true]
// За iOS — в Podfile: :hermes_enabled => true
За диагностика на размера на бъндъла, Metro предоставя флаг --bundle-output с статистически изход. Използвайте bundle-visualizer за анализ на състава на бъндъла и намиране на големи модули, които могат да се зареждат мързеливо. Редовната проверка на размера на бъндъла трябва да бъде част от CI/CD тръбопровода на React Native проекта.
Metro поддържа делта-бъндълове — механизъм, при който след първото пълно компилиране сървърът изпраща само промените (делта) между старата и новата версия на бъндъла. Това радикално ускорява последващите компилации: времето за актуализиране намалява от секунди на десетки милисекунди. Делта-бъндъловете са особено ефективни на етапа на разработка, когато разработчикът често запазва промените и презарежда приложението.
За включване на делта-бъндълове в React Native се използва флагът --delta в командата npx react-native bundle. От страна на клиента, делта-бъндъловете се поддържат от React Native 0.64. В производствените компилации делта-бъндъловете не се прилагат — вместо тях се използва пълен бъндъл с Hermes байткод за максимална производителност при стартиране. Това се постига, защото увеличаването на скоростта на разработка компенсира разходите за първото пълно компилиране.
Hermes — JavaScript двигател, разработен от Meta специално за React Native. Metro генерира Hermes байткод на етапа на компилирането, което позволява на приложението се стартира без скъпа компилация на JavaScript на устройството. За включване на Hermes е достатъчно да добавите enableHermes: true в metro.config.js и да настроите build.gradle или Podfile. Hermes намалява времето за стартиране на приложението с 30–50% и размера на APK с 20–30% в сравнение с JavaScriptCore.
При използване на Hermes е важно да помните ограниченията: двигателът не поддържа Proxy, Reflect и някои възможности на ES6. Повечето React Native приложения не използват тези възможности директно, но някои библиотеки могат да причинят конфликти. Преди да включите Hermes, проверете съвместимостта на всички зависимости на проекта чрез официалния контролен списък за съвместимост. Metro автоматично преминава в режим на генериране на байткод при активация на Hermes.
За проследяне на ефективността на компилацията на Metro, използвайте вградените метрики, достъпни чрез Flipper — инструмента за отлаждане на React Native. Metro публикува събития: bundle_request, transform, resolve с времето за изпълнение на всяка фаза. Анализът на тези данни помага да се идентифицират тесните места: ако фазата transform отнима повече от 70% от времето, проблемът е в Babel трансформера. Включете воденето на дневници на Metro чрез флага --verbose за подробна диагностика.
Често задавани въпроси
Технически да — съществуват експериментални проекти като react-native-webpack, но те не са официално поддържани. Metro е интегриран с React Native bridge, Turbo Modules и Hermes на ниво, недостъпен за Webpack. Замяната на Metro ще доведе до загуба на Fast Refresh и официална поддръжка.
React Native не използва CSS за стилизация — вместо това се използва JavaScript стилизация чрез StyleSheet.create. Тъй като Metro е създаден изключително за React Native, поддръжката на CSS не е необходима. За уеб рендиране чрез React Native Web, CSS се обработва от отделни инструменти ивън Metro.
Metro не обработва изображения или шрифтове като модули. Ресурсите се свързват чрез require('./image.png'), но Metro ги регистрира само като низови зависимости. Реалното зареждане на изображенията се извършва от нативния код на React Native чрез компонента Image, а Metro копира ресурсите в бъндъла.
Rollup генерира ES модули с tree shaking на ниво модул, което дава минимален размер на бъндъла за библиотеки. Metro генерира CommonJS бъндъл с inline трансформация и оптимизация за мобилни двигатели. Rollup не поддържа Fast Refresh и не може да работи с нативни модули на React Native.
Студената компилация на Metro се забавя поради пълното скениране на node_modules. Решения: добавете watchFolders за монорепозиториумите, използвайте maxWorkers за паралелна трансформация и инсталирайте Watchman. За проекти с 10 000+ файла, помислете за metro-memory-fs за кеширане в паметта.
Обобщение
watchFolders и extraNodeModules за правилно решаване на модулите.Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също