内容简介:React Native是一个跨平台的使用JavaScript编写最终生成移动端原生控件应用的开源框架。下面是根据React Native在iOS端的情形下,对项目启动时,所运行的脚本进行解读。 当我们使用React Native的命令行工具创建一个项目( react-native init myProject ),在启动项目项目(执行不管我们是通过命令行启动App还是通过Xcode启动App,最终都会执行编译的过程。而在编译我们项目的Target时,仍会执行React Native帮我们插入的脚本。脚本的
React Native是一个跨平台的使用JavaScript编写最终生成移动端原生控件应用的开源框架。下面是根据React Native在iOS端的情形下,对项目启动时,所运行的脚本进行解读。 当我们使用React Native的命令行 工具 创建一个项目( react-native init myProject ),在启动项目项目(执行 react-native start 命令 或 使用Xcode打开ios项目, CMD + R 运行项目)时,React Native会默认帮我们启动一个名为Packager的Node服务,同时在iOS项目编译过程中还会执行 react-natvie-xcode.sh 的脚本。下面我们将对这两部分的脚本进行一一解读。
启动Packager服务
-
Packager的意义
它是一个本地的Node服务,我们在开发过程中(DEBUG模下)获取的js代码其实是这个服务器返回的。我们在修改js代码时,Packager服务会动态的同步我们的代码,然后App再去请求Packager服务获取我们修改的最新代码。这一过程就避免了我们在开发过程中,频繁进行打包的过程。Packager为我们提供了一个高效的代码动态加载方式。
-
脚本启动入口
-
使用命令行启动 如果我们启动的方式是使用命令行方式启动的,那么在这个命令背后做的工作之一就是启动Packager。启动命令如下:
react-native run-ios 复制代码
-
在iOS项目中启动 如果我们是通过XCode打开iOS项目,然后在执行快捷键CMD + R命令运行项目,此时也会自动的启动Packager,那么启动Packager的入口在哪里呢?参见Xcode界面的截图:
通过上面的截图标注的路径,我们可以看到通过Xcode在编译React这个工程时,会去执行一段 shell 脚本来启动我们的Packager服务。
-
shell脚本解读
- RCT_METRO_PORT 变量为Packager的服务的端口号,将这个变量赋值 8081
- 将 RCT_METRO_PORT=8081 这段字符串写入到node_modules/react-native/scripts/.packager.env路径下
- 判断指定的端口号(8081)是否被占用了:
- 如果被占用了:则向这个端口号发送请求(请求的url: http://localhost:8081/status/),如果返回字符串为 packager-status:running 则说明Packager已经启动了。否则就退出脚本,返回退出码2。(注意:在编译过程中,执行的脚本退出码为非零,则编译就会退出并报错)。
- 如果没有被占用:则执行node_modules/react-native/scripts/launchPackager.command路径下的脚本。为了更直观的感受script文件夹下都有哪些东西,可见下图:
- launchPackager.command脚本其实只做了非常简单的事情,只是将打印形式以及终端标题修改,然后再执行同级目录下的 packager.sh 脚本。
- packager.sh脚本也仅有几行代码。在读取前面讲到的 .packager.env 文件,获取端口号之后,通过执行Node命令执行与scripts文件夹同级的local-cli文件夹下的 cli.js 文件,并传入当前脚本的所有参数。最终达到了与执行命令行时的效果———启动Packager服务。
-
-
小结 通过iOS项目启动Packager服务的脚本解读,我们知道了在iOS项目中,启动脚本的入口在哪,以及层层递进找到了真正启动Packager服务的文件在哪里。在iOS项目中的这些设置,是React Native的命令行在生成项目时,为我们做的工作。 如果我们是在原生项目中集成RN的话,我们就不得不自己手动做这些操作了。大多数情况下我们会使用CocoaPods来管理第三方的依赖库(当然也包括React Native)。因此对于启动Packager的脚本放置的位置应该是在Pods工程下了。
执行打包操作
不管我们是通过命令行启动App还是通过Xcode启动App,最终都会执行编译的过程。而在编译我们项目的Target时,仍会执行React Native帮我们插入的脚本。脚本的入口如下图:
我们可以看到,在这段脚本中,主要执行了 react-native-xcode.sh文件中的脚本。 下面就针对脚本的执行逻辑进行解读:
- 获取项目编译的路径,即编译时生成.app包的路径
- 根据项目配置(CONFIGURATION)判断是否是DEBUG模式,同时判断运行的平台(PLATFORM_NAME)是否 不是 模拟器(即是否在真机上)。若两个条件同时满足,则获取本机在当前局域网下的ip地址,并将这个ip地址写入到.app包路径下的ip.txt文件中。 说明 :这个ip.txt会用在真机调试下。在App启动时,在RCTBundleURLProvider对象寻找jsbundle路径时用到。此时返回Packager的URL的主机名不再是localhost,而是从ip.txt中获取的ip地址
- 判断环境变量 SKIP_BUNDLING 是否有值,如果有值,则退出脚本。不再进行后续的操作了。 说明 :这个参数在React Native初始化项目时,并没有给我们在iOS项目中显式的表达出来。需要我们自己指定,隐藏的很深啊!
- 针对环境变量 CONFIGURATION 的值,进行以下逻辑判断:
- 值为DEBUG ;判断运行的平台是否是模拟器。如果是模拟器,则再判断环境变量 FORCE_BUNDLING 是否有值,如果有值则继续走打包的流程;如果没值,则退出脚本。这样做的目的是想要判断是否在模拟器下也进行打包操作(通过 FORCE_BUNDLING 来判断)。
- 值为空"" ;说明该脚本并不是在Xcode编译时运行的,因此也没必要再执行后续的操作了,停止指定脚本,退出码1。
- 其他值 ;说明此时的编译配置是Release模式,是需要打包成AdHoc包或上传到AppStore的。此时将脚本中的变量 DEBUG 的值设置为 false (在其他分支上DEBUG都为true),这个值在后面执行打包操作时,会作为参数传给打包命令。
- 获取在node_modules文件夹下的react native文件夹的目录,以及我们创建的项目的根目录,并且切换路径(cd)到根目录。
- 获取根目录下的入口文件名,这个文件名可能是 index.js (在较高RN版本项目中),也可能是 index.ios.js (在较低RN版本项目中)
- 判断node环境是否存在,如果不存在的话,就退出脚本,退出码2,编译报错。
- 获取执行打包命令的文件cli.js路径,即是/react native/cli.js路径
- 获取打包的命令;即是 bundle 命令
- 执行打包命令;即运行node,执行cli文件,将之前准备的参数拼接好传递进去。 打出来的main.jsbundle以及asserts文件夹都会放在编译生成的.app文件夹下,也即是放入到App包内了。
小结:
通过以上流程,我们知道了在iOS项目中,执行打包脚本的入口在哪里,以及打包脚本react-native-xcode.sh内部做了哪些操作。其实归根结底也就执行了我们日常使用命令行工具打包的 bundle命令 ,打出来的jsbundle和asserts文件直接放入到了.app包内了。
react-native bundle --entry-file index.js --platform ios --dev false --bundle-output "导出路径/main.jsbundle" --assets-dest "导出路径" 复制代码
这一打包脚本中,值得我们关注的几个变量:
- SKIP_BUNDLING : 是否跳过打包操作。 我们在真机调试的过程中,一般都会将手机的wifi连接到与电脑同一局域网下(也可以说是同一wifi下),这样手机才能访问到Packager服务,进而获取服务器的js代码。如果调试的手机的网络和Packager服务(通常是运行项目的电脑)不是在同一局域网下,那么运行App时,由于访问不到Packager服务器,在请求失败后,App就会加载我们在编译过程生成的js包,从而避免崩溃。这套流程非常完美的解决了我们在真机调试下,不管手机处于什么网络,都能够正常的启动App了。 尽管React Native尽心尽力的帮我们解决在开发中我们做得不规范操作所导致的App运行不起来的原因。但是这样也给我们带来了额外的开销。 在大多数开发情况下,我们进行真机调试时,都会意识到将手机的网络设置成与电脑的网络处于同一局域网下(通俗点讲是:要设置成同一wifi下)。我们明确我们的目标是让App接连到Packager服务,而并非本地的jsbundle代码。但是我们却意外的承受了打包jsbundle所带来的额外时间开销。 从另一方面来说,假设我们的手机处于与Packager服务不同的局域网下,在App启动时,会发送请求去验证Packager服务是否正常运行,在等待相应的过程中是异常的漫长,默认情况下我们需要等待60秒后(请求超时),返回结果失败了,才会去加载本地的jsbundle代码,App才能启动。此时我们花费的等待时间就太漫长了。 如果开发者不愿意承受这种额外的开销。那么 SKIP_BUNDLING 变量就给了我们避免额外开销的一种解决方式。我们只需要在iOS项目执行打包脚本的入口将SKIP_BUNDLING的值设置为true即可。代码如下:
export SKIP_BUNDLING=true 复制代码
最终入口的截图:
2. FORCE_BUNDLING :该变量用来表明在模拟器上运行时,是否也要进行打包操作。 我们已经知道App启动时,去哪里加载js代码的规则。有些时候,我们想在在模拟器上直接运行我们的App,但是我们又想启动Packager服务。那么我们就可以使用这个变量来让我们在编译App时,也需要将js代码打包到模拟器的.app包内。就可以达到这样的目的了 设置的方法如同前面介绍的方法一样。只需要将 export FORCE_BUNDLING=true代码放入到iOS项目执行打包脚本的入口即可。
以上所述就是小编给大家介绍的《React Native启动脚本解读、优化》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 脚本文件里的 Hybrid Script(混合式脚本)
- Phoenix解读 | Phoenix源码解读之索引
- Phoenix解读 | Phoenix源码解读之SQL
- 脚本错误量极致优化-定位压缩且无 SourceMap 文件的脚本错误
- 如何从PHP脚本(如批处理文件)中运行多个PHP脚本?
- 荐 python脚本如何监听终止进程行为,如何通过脚本名获取pid
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Elements of Programming
Alexander A. Stepanov、Paul McJones / Addison-Wesley Professional / 2009-6-19 / USD 39.99
Elements of Programming provides a different understanding of programming than is presented elsewhere. Its major premise is that practical programming, like other areas of science and engineering, mus......一起来看看 《Elements of Programming》 这本书的介绍吧!