2020-06-02 React Native 打包工具Metro原理探究

Metro 是什么

文档:Metro

metro 是一个针对 React NativeJavaScript模块打包器,他接收一个entry file (入口文件) 和一些配置作为参数,返回给你一个单独的JavaScript文件,这个文件包含了你写的所有的JavaScript 代码和所有的依赖。

也就是说Metro把你写的几十上百个js文件和几百个node_modules的依赖,打包成了一个文件。

Metro的工作原理

Metro 的打包过程有3个独立的阶段

  • Resolution
  • Transformation
  • Serialization

Resolution 阶段

Metro 需要建立一个你的入口文件所需要的所有的模块的表,为了找到一个文件依赖了哪些文件,Metro 使用了一个resolver。在实际中,Resolution阶段是和transformation阶段并行进行的。

Transformation阶段

所有的模块都要经历一个 transformertransformer 负责把一个模块转换成RN能理解的格式;

Serialization阶段

一旦模块被转换完成,就会马上被serialized,通过serializer,把上一个阶段转换好的模块组合成一个或多个bundlebundle 就是字面意思:把一堆模块组合成一个单独的JavaScript文件


Metro这个库已经根据bundle时的各个阶段,拆分为resolver,transformer,serializer 模块了,每个模块负责相应的功能,因此你可以方便的替换为自己的模块。

RAM bundle

即: Random Access Modules (RAM) bundle
当 Metro在bundle的时候,每个模块都被分配了一个数字的id,这意味着Metro不支持动态的require
Requires 随着数字的版本而改变,从而modules 被分成了不同的形式,目前支持3种不同形式的bundling

  1. Plain bundle

这是标准的bundling 形式,所有的文件是通过一个立即执行函数包裹,然后加到global file 中,这中形式对于一些期望JS only bundle的环境(比如浏览器环境)是很有用的;用.bundle后缀来 requiring 入口文件就会触发一个Plain bundle的构建

  1. 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
  1. File RAM bundle

每个模块都被保存为名为 js-modules/${id}.js的文件,另外还创建了一个名为UNBUNDLE的文件,内容只包含magic number0xFB0BD1E5,注意 UNBUNDLE文件位于根目录下,这种bundling通常用于Android端,因为package contents 被压缩了,Android 加载压缩文件十分快,如果使用 indexed format,所有的bundled需要一次性被解压出来才能获取到对于模块的代码

如何开启RAM bundle

默认情况下,打包的bundlePlain bundle,后面的2种RAM bundle的形式是高级用法。
使用方法见文档:enable-the-ram-format

你可能感兴趣的:(2020-06-02 React Native 打包工具Metro原理探究)