在使用 React Native 开发跨平台 App 的过程中,我们每天都会看到一个熟悉的命令:
npx react-native start

当命令行窗口里跳出那只「奔跑的小火车」后,你的应用才能顺利运行起来。
这列小火车其实就是 Metro ------ React Native 的默认 JavaScript 打包器与开发服务器。
虽然 Metro 平时"默默无闻",但它几乎影响着每次调试、热更新、打包、预览的体验。
本文将系统介绍 Metro 是什么、它的原理、它能做什么、常见配置方式以及它与 Webpack/Vite 的区别。
一、Metro 是什么?
Metro 是 React Native 官方提供的 JavaScript bundler(打包器)与 dev server(开发服务器)。
它的核心目标很明确:
- 快速(Fast)
- 可靠(Reliable)
- 适用于移动端(Optimized for RN)
Metro 并不是浏览器端的 bundler,而是专门为 React Native 的运行时(Hermes 或 JSC)和移动设备环境 设计的。
因此它用的并非标准的 ES module,而是 Facebook 自己的模块格式(Metro bundler format)。
二、Metro 的工作原理
Metro 的整体 pipeline(处理流程)如下:
1. 模块解析(Module Resolution)
Metro 会解析 JS 文件之间的依赖,包括:
- ES6 import / export
- require()
- 动态 require
- React Native 的 platform extensions(如
.ios.js、.android.js) - node_modules 中的第三方依赖
Metro 会生成整个依赖图(Dependency Graph)。
2. 转译(Transformation)
每个模块交给 Metro 内置的 transformer 处理:
- Babel 转译 ES6/TS → RN 支持的 JavaScript
- 处理 JSX
- Flow 类型剥离
- Source map 生成
你也可以通过自定义 metro.config.js 替换 transformer(例如使用 SWC)。
3. 打包(Bundling)
Metro 会把所有模块按照依赖顺序组装成一个 单一 bundle:
- 生产环境:
index.android.bundle/index.ios.bundle - 开发调试环境:实时生成、带 Source Map
Metro 会进行:
- 模块编号映射(module id)
- 合并 require
- 代码压缩(Terser)
- 去除不可达代码(Dead code elimination)
最重要的是 Metro 打出来的 bundle,是一个 React Native 特定格式的结构:
js
__d(function (global, require, module, exports) {
// module content
});
移动端的 JS 引擎用这个机制来加载模块。
4. 提供开发功能(Dev Server)
Metro 也是一个 dev server,支持:
- Fast Refresh(RN 的热更新机制)
- Source Map 调试
- 打包增量构建
- 真机 / 模拟器实时加载变化
- 向 APP 推送 JS 更新
在开发模式下,极大提升迭代效率。
三、Metro 的核心特性
1. 启动速度快
Metro 对 RN 项目最常用的文件(JS/TS/JSON)进行高度优化,依赖图计算快,增量打包也快。
2. 高并发、增量构建
Metro 会把静态分析出来的依赖存到磁盘(缓存到 .metro),当文件变化时只重新构建受影响部分。
3. 多平台打包
自动处理文件扩展名:
Button.ios.js
Button.android.js
Button.native.js
Button.js
无需人工处理。
4. 可扩展性强
你可以扩展:
- transformer(Babel/SWC/自定义)
- resolver(文件后缀)
- serializer(自定义 bundle 输出)
- server hooks(拦截请求)
四、Metro 配置示例(metro.config.js)
标准配置长这样:
js
// metro.config.js
const { getDefaultConfig } = require("metro-config");
module.exports = (async () => {
const {
resolver: { sourceExts, assetExts }
} = await getDefaultConfig();
return {
transformer: {
babelTransformerPath: require.resolve("react-native-svg-transformer"),
},
resolver: {
assetExts: assetExts.filter(ext => ext !== "svg"),
sourceExts: [...sourceExts, "svg"],
}
};
})();
这是一个常见的例子:让 Metro 支持 SVG 文件。
其他常见用途包括:
- 支持 TypeScript path alias
- 集成 monorepo(Yarn Workspace、pnpm)
- 自定义打包输出路径
五、Metro vs Webpack vs Vite
| 能力 | Metro | Webpack | Vite |
|---|---|---|---|
| 使用场景 | React Native | Web / Electron | Web |
| 打包速度 | 快 | 可优化、中等 | 极快(基于 esbuild) |
| 热更新 | Fast Refresh | HMR | HMR |
| 是否支持原生模块系统 | ✔ RN Bundle | ✘ | ✘ |
| 按平台打包 | ✔(ios/android/...) | ✘ | ✘ |
| 是否支持 tree shaking | ✔(RN 语义) | ✔ | ✔ |
本质区别:
- Metro 不面向浏览器 ,它输出的不是 browser bundle,而是 React Native runtime bundle。
- Metro 非常适合移动端大 bundle、低内存场景。
- Vite/webpack 无法直接替代 Metro(除非你 hack RN)。
六、什么时候需要关注 Metro?
以下情况你一定会接触 Metro:
- 你在 RN 中使用 monorepo(比如 NX / TurboRepo)
- 你要支持 React Native Web / SSR
- 你在优化 RN 首屏启动
- 你在集成一些特殊文件格式(svg、wasm、自定义扩展名)
- 打包体积太大,想做 bundle 拆分/分析
- iOS/Android 生产环境 bundle 构建优化
如果你遇到如下情况:
- 安卓 / iOS bundle 太大
- Metro 卡死、重启过慢
- monorepo 导致 "Unable to resolve module"
- RN Hermes 报错但无法定位
- Fast Refresh 失效
那大概率都和 Metro 有关。
七、如何分析 Metro 打出来的 bundle?
Metro 提供 bundle 命令:
npx react-native bundle \
--entry-file index.js \
--platform android \
--bundle-output ./dist/index.android.bundle \
--dev false
你可以配合:
- react-native-bundle-visualizer
- source-map-explorer
来分析依赖树和体积。
总结:Metro 是 RN 的灵魂基础设施
Metro 是 React Native 的核心底层组件,它融合了:
- 打包器(Bundler)
- 模块解析器(Resolver)
- Babel/SWC 转译器(Transformer)
- 开发服务器(Dev Server)
- 实时热更新(Fast Refresh)
表面上只是一列小火车,它的任务却贯穿了 RN 开发的每一个环节。
理解 Metro,有助于开发者:
- 更好地优化 RN 性能
- 更快定位问题
- 更容易扩展 RN 工程能力(monorepo、跨端)
如果你正在做复杂架构、RN 大型应用、性能优化,Metro 都是绕不过去的一环。