React-Native应用程序的内容是由Javascript语言开发的,而Android或者IOS手机系统只是一个容器和各类服务提供者。
众所周知,Javascript是一门解释型脚本语言,对于浏览器而言,浏览器负责解释和执行Javascript脚本。而对于手机系统而言,同样是负责解释和执行Javascript脚本,当然其核心都是使用的webkit内核。
浏览器获取Javascript脚本,主要通过网络下载 + 本地缓存的机制,达到效率的最大化。当然,移动应用也不例外,但不同的是移动应用可以将Javascript脚本直接打包在应用程序内,免去网络下载这个极其不稳定的过程,这样可以达到加载效率和性能流畅的最大化,也就是风靡一时Hybrid技术,而这一点浏览器是做不到的。
无论使用网络下载还是本地文件,最终都是要加载JS文件,而React-Native项目中包含大量的JS文件构成的框架和组件,那么Android框架又是如何去加载它们的呢?这个过程就是本篇博客的研究的主题了!
1、JS文件的整合
有这样一个常识:拷贝100个1M的文件,比拷贝1个100M的文件要慢的多。
一个React-Native项目中,包含有成百上千个JS文件,可以想象,如果一次性加载(读)这么多个文件,其效率将会极其低下。但是如果将这些JS文件预先合并成一个文件,然后去加载,其效率肯定能提高很多。
当所有相关的JS文件合并成一个文件后,还需要进行优化。包括去除空格和换行符、代码混淆等,这样处理之后会有两个好处:
1、大幅减小文件大小,无论是对加载效率还是应用体积,好处都是莫大的。
2、提高应用程序的安全性,防止反编译等。
那么,React-Native框架是如何整合JS文件的呢?
首先,需要知道一点,这个整合过程肯定是极其缓慢的,毕竟涉及上千个文件,所以不能是放在应用程序内进行,最合适的做法是预处理,即时机放在打包或者编译时。
另外,Javascript前端开发的模式流程和移动应用开发的模式流程是完全不一样的。Javascript开发者,不需要反复的打包安装应用,对他们而言,一个解释执行器(比如浏览器)就够了,所有的代码都直接放在本地服务器。
React-Native很好地遵循了这一模式,一次安装的应用程序作为解释执行器,nodejs服务器作为本地服务器,所有的JS文件全部部署在这个服务器上。前端开发者修改完代码,直接在应用程序上reload一下就能看到结果。这种模式,对前端开发者来说几乎不要学习什么,完全是轻车熟路的。
所以,JS整合的工作,自然就是交给nodejs服务器来做了!整合过程的细节不是本博客的重点,就不去分析了。
如果是正式发布包,在应用运行时,是不存在本地nodejs服务器这个概念的,所以JS整合文件都是预先打包到assets资源文件里的。下面,来看下这个打包过程。
对JS整合文件的打包逻辑,位于项目\android\app\react.gradle
...
def devEnabled = !targetName.toLowerCase().contains("release")
commandLine "cmd", "/c", "react-native", "bundle", "--platform", "android", "--dev", "${devEnabled}", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir
...
在gradle打包流程里面插入一个自定义Task任务,即在命令行中运行react-native bundle命令,整合和优化JS文件,存放到assets资源文件目录中。
来看一下react-native bundle命令的参数。
–entry-file: 应用入口文件,默认为项目根目录下的index.android.js或index.ios.js
–platform:系统平台,android或者ios选其一
–transformer: babel转换器,默认使用\node_modules\react-native\packager\transformer.js
–dev:是否开发模式,默认开启,此时不会进行JS混淆和压缩优化,方便开发者调试。
–bundle-output: 最终整合的输出文件名,一般是index.android.bundle或index.ios.bundle
–bundle-encoding:整合文件的编码格式,默认utf-8
–assets-dest:整合文件存储目录,android打包时会定义为项目的assets资源编译临时目录。
所以,Android项目打正式包的时候,运行的命令如下:
react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output index.android.bundle -</