Metro 是什么
文档:Metro
metro
是一个针对 React Native
的JavaScript
模块打包器,他接收一个entry file
(入口文件) 和一些配置作为参数,返回给你一个
单独的JavaScript
文件,这个文件包含了你写的所有的JavaScript
代码和所有的依赖。
也就是说Metro
把你写的几十上百个js
文件和几百个node_modules
的依赖,打包成了一个文件。
Metro的工作原理
Metro 的打包过程有3个独立的阶段
- Resolution
- Transformation
- Serialization
Resolution
阶段
Metro
需要建立一个你的入口文件所需要的所有的模块的表,为了找到一个文件依赖了哪些文件,Metro
使用了一个resolver
。在实际中,Resolution
阶段是和transformation
阶段并行进行的。
Transformation
阶段
所有的模块都要经历一个 transformer
, transformer
负责把一个模块转换成RN
能理解的格式;
Serialization
阶段
一旦模块被转换完成,就会马上被serialized
,通过serializer
,把上一个阶段转换好的模块组合成一个或多个bundle
,bundle
就是字面意思:把一堆模块组合成一个单独的JavaScript
文件
Metro
这个库已经根据bundle
时的各个阶段,拆分为resolver
,transformer
,serializer
模块了,每个模块负责相应的功能,因此你可以方便的替换为自己的模块。
RAM bundle
即: Random Access Modules (RAM) bundle
当 Metro在bundle
的时候,每个模块都被分配了一个数字的id
,这意味着Metro
不支持动态的require
;
Requires
随着数字的版本而改变,从而modules
被分成了不同的形式,目前支持3种
不同形式的bundling
- Plain bundle
这是标准的bundling
形式,所有的文件是通过一个立即执行函数包裹,然后加到global file 中,这中形式对于一些期望JS only bundle
的环境(比如浏览器环境)是很有用的;用.bundle
后缀来 requiring 入口文件就会触发一个Plain bundle
的构建
- Indexed RAM bundle
这种形式是把bundle
变成了二进制文件,文件一般由以下部分组成:
-
A magic number:文件的开头必须是一个
uint32
,他的值是0xFB0BD1E5
,这个用于验证文件; -
An offset table:这个table是一系列的
uint32 pairs
,还带有一个header
-
header:header是2个
uint32
,一个指示了这个table
的长度,另一个指示了startup code
的长度 -
pairs:他们以
bytes
的形式指示了文件的offset
,模块的长度
` 0 1 2 3 4 5 6
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Magic number | Header size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Startup code size | Module 0 offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Module 0 length | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
| |
+ ... +
| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | Module n offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Module n length | Module 0 code | Module 0 code | ... | \0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Module 1 code | Module 1 code | ... | \0 | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
| |
+ ... +
| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | Module n code | Module n code | ... | \0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+`
- 通过使用
offset table
,我们可以在固定时间内加载任意的module
:比如module x 位于file[(x + 3) * sizeof(uint32)]
,由于模块是通过\0
来区分开的,一般情况下甚至不需要使用length
,module能直接以ASCIIZ string
的方式加载 -
Startup code
总是在file[sizeof(uint32)]
这种结构对于可以一次把所有代码加载到内存的环境是最佳的;这种bundling
通常用于iOS
端
- File RAM bundle
每个模块都被保存为名为 js-modules/${id}.js
的文件,另外还创建了一个名为UNBUNDLE
的文件,内容只包含magic number
:0xFB0BD1E5
,注意 UNBUNDLE
文件位于根目录下,这种bundling
通常用于Android
端,因为package contents
被压缩了,Android
加载压缩文件十分快,如果使用 indexed format
,所有的bundled
需要一次性被解压出来才能获取到对于模块的代码
如何开启RAM bundle
默认情况下,打包的bundle
是 Plain bundle
,后面的2种RAM bundle
的形式是高级用法。
使用方法见文档:enable-the-ram-format
网友评论