rn打包分析
rn打包原来是packager,后来独立出一个专门的打包工具metro,构建工具的大体思路跟前端构建工具差不多,都会有一个启动文件,然后根据模块依赖关系把对应文件找到。
开发中打包
在开发中打包,我们访问的是一个index.bundle,这个时候跟其他的前端构建工具一致,都是会启动一个node 服务,metro提供了一个createServer方法,这个方法结合http(s)模块构建出一个可以提供bundle文件的服务器,当然可以其他的node框架,比如express:
const express = require('express');
const app = express();
app.use(
metroBundlerServer.processRequest.bind(metroBundlerServer),
);
app.listen(8081);
我们可以在浏览器中输入http://localhost:8081/index.bundle?platform=ios&dev=true
那么这个bundle文件就会被返回,而metroServer会提供几个方法来提供返回内容,可以是bundle也可以是其他静态资源,这里我们访问的是bundle,那么就会使用processRequestBundle方法,这个方法会根据访问的url去找对应的js文件,然后打包成bundle文件
所以这里的虚拟目录根目录就是项目根目录,我们可以独立的去访问某个目录下js文件,都会为我们打包成bundle文件返回。
bundle文件
这里简单分析下bundle文件,我们简单的翻一下整个文件,就可以发现,前面除了第一行是设置环境变量,其他都是一些自执行函数,后面的全是_d函数,大致也可以猜出来,_d函数就是模块定义函数。我们看前面的函数,第一个自执行函数里面有个很熟悉的东东,_d函数找到了,它被挂在到global下,在这个函数中把模块加载相关做了定义。
模块的id是一个createModuleIdFactory方法,然后我们看到第一个定义的模块id是从11开始的,这是因为之前的polyfill文件生成也在调用这个函数。
function createModuleIdFactory() {
const fileToIdMap = new Map();
let nextId = 0;
return path => {
let id = fileToIdMap.get(path);
if (typeof id !== 'number') {
id = nextId++;
fileToIdMap.set(path, id);
}
return id;
};
}
而后面的几个函数我们可以看到都是各种es6语法的polyfill,其中有个函数很特别,先是定义了一个inspect函数,里边大量的数据类型判断,之后重新定义了console方法,这里其实是对console做了polyfill,但是由于rn的打印错误信息跟浏览器有不一致,所以前面会有很多数据类型判断,来保持跟浏览器控制台中打印的一致。
之后的紧接着就是我们的项目启动模块,但是之后的就是rn、react以及其他依赖项目的模块,一致到所有的公共模块加载完才是我们的业务模块。
本地打包
本地打包也就是打包出实实在在的bundle文件,而不是虚拟目录下,命令使用这里就不说了,这里要注意的一点就是,如果你想在某个目录下存放你的bundle文件,这个目录一定要存在,metro不会帮你去创建目录。这个时候调用依然会是metro的server,只不过最终会输出bundle文件到指定目录。
打包后的bundle文件跟开发中的结构是一致的,但是我们仔细看就会发现模块id变少了,前面的polyfill文件一样的,都是第一个模块ID都是11,也就是启动文件,那么就是中间的模块便少了,其实就是一些公共模块做了合并,所以看起来少了。
总结
基本来说只要rn和react等基础包版本不变,那么打包出来的bundle文件公共部分就是相同的,这里也利于我们进行基础包也业务包的分离。