React Native启动脚本解读、优化

栏目: 服务器 · 发布时间: 5年前

内容简介: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服务

  1. Packager的意义

    它是一个本地的Node服务,我们在开发过程中(DEBUG模下)获取的js代码其实是这个服务器返回的。我们在修改js代码时,Packager服务会动态的同步我们的代码,然后App再去请求Packager服务获取我们修改的最新代码。这一过程就避免了我们在开发过程中,频繁进行打包的过程。Packager为我们提供了一个高效的代码动态加载方式。

  2. 脚本启动入口

    1. 使用命令行启动 如果我们启动的方式是使用命令行方式启动的,那么在这个命令背后做的工作之一就是启动Packager。启动命令如下:

      react-native run-ios
      复制代码
    2. 在iOS项目中启动 如果我们是通过XCode打开iOS项目,然后在执行快捷键CMD + R命令运行项目,此时也会自动的启动Packager,那么启动Packager的入口在哪里呢?参见Xcode界面的截图:

      React Native启动脚本解读、优化

      通过上面的截图标注的路径,我们可以看到通过Xcode在编译React这个工程时,会去执行一段 shell 脚本来启动我们的Packager服务。

    3. shell脚本解读

      1. RCT_METRO_PORT 变量为Packager的服务的端口号,将这个变量赋值 8081
      2. RCT_METRO_PORT=8081 这段字符串写入到node_modules/react-native/scripts/.packager.env路径下
      3. 判断指定的端口号(8081)是否被占用了:
        1. 如果被占用了:则向这个端口号发送请求(请求的url: http://localhost:8081/status/),如果返回字符串为 packager-status:running 则说明Packager已经启动了。否则就退出脚本,返回退出码2。(注意:在编译过程中,执行的脚本退出码为非零,则编译就会退出并报错)。
        2. 如果没有被占用:则执行node_modules/react-native/scripts/launchPackager.command路径下的脚本。为了更直观的感受script文件夹下都有哪些东西,可见下图:
          React Native启动脚本解读、优化
      • launchPackager.command脚本其实只做了非常简单的事情,只是将打印形式以及终端标题修改,然后再执行同级目录下的 packager.sh 脚本。
      • packager.sh脚本也仅有几行代码。在读取前面讲到的 .packager.env 文件,获取端口号之后,通过执行Node命令执行与scripts文件夹同级的local-cli文件夹下的 cli.js 文件,并传入当前脚本的所有参数。最终达到了与执行命令行时的效果———启动Packager服务。
  3. 小结 通过iOS项目启动Packager服务的脚本解读,我们知道了在iOS项目中,启动脚本的入口在哪,以及层层递进找到了真正启动Packager服务的文件在哪里。在iOS项目中的这些设置,是React Native的命令行在生成项目时,为我们做的工作。 如果我们是在原生项目中集成RN的话,我们就不得不自己手动做这些操作了。大多数情况下我们会使用CocoaPods来管理第三方的依赖库(当然也包括React Native)。因此对于启动Packager的脚本放置的位置应该是在Pods工程下了。

执行打包操作

不管我们是通过命令行启动App还是通过Xcode启动App,最终都会执行编译的过程。而在编译我们项目的Target时,仍会执行React Native帮我们插入的脚本。脚本的入口如下图:

React Native启动脚本解读、优化
我们可以看到,在这段脚本中,主要执行了 react-native-xcode.sh

文件中的脚本。 下面就针对脚本的执行逻辑进行解读:

  1. 获取项目编译的路径,即编译时生成.app包的路径
  2. 根据项目配置(CONFIGURATION)判断是否是DEBUG模式,同时判断运行的平台(PLATFORM_NAME)是否 不是 模拟器(即是否在真机上)。若两个条件同时满足,则获取本机在当前局域网下的ip地址,并将这个ip地址写入到.app包路径下的ip.txt文件中。 说明 :这个ip.txt会用在真机调试下。在App启动时,在RCTBundleURLProvider对象寻找jsbundle路径时用到。此时返回Packager的URL的主机名不再是localhost,而是从ip.txt中获取的ip地址
  3. 判断环境变量 SKIP_BUNDLING 是否有值,如果有值,则退出脚本。不再进行后续的操作了。 说明 :这个参数在React Native初始化项目时,并没有给我们在iOS项目中显式的表达出来。需要我们自己指定,隐藏的很深啊!
  4. 针对环境变量 CONFIGURATION 的值,进行以下逻辑判断:
    1. 值为DEBUG ;判断运行的平台是否是模拟器。如果是模拟器,则再判断环境变量 FORCE_BUNDLING 是否有值,如果有值则继续走打包的流程;如果没值,则退出脚本。这样做的目的是想要判断是否在模拟器下也进行打包操作(通过 FORCE_BUNDLING 来判断)。
    2. 值为空"" ;说明该脚本并不是在Xcode编译时运行的,因此也没必要再执行后续的操作了,停止指定脚本,退出码1。
    3. 其他值 ;说明此时的编译配置是Release模式,是需要打包成AdHoc包或上传到AppStore的。此时将脚本中的变量 DEBUG 的值设置为 false (在其他分支上DEBUG都为true),这个值在后面执行打包操作时,会作为参数传给打包命令。
  5. 获取在node_modules文件夹下的react native文件夹的目录,以及我们创建的项目的根目录,并且切换路径(cd)到根目录。
  6. 获取根目录下的入口文件名,这个文件名可能是 index.js (在较高RN版本项目中),也可能是 index.ios.js (在较低RN版本项目中)
  7. 判断node环境是否存在,如果不存在的话,就退出脚本,退出码2,编译报错。
  8. 获取执行打包命令的文件cli.js路径,即是/react native/cli.js路径
  9. 获取打包的命令;即是 bundle 命令
  10. 执行打包命令;即运行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 "导出路径"
复制代码

这一打包脚本中,值得我们关注的几个变量:

  1. 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
复制代码

最终入口的截图:

React Native启动脚本解读、优化
2. FORCE_BUNDLING :该变量用来表明在模拟器上运行时,是否也要进行打包操作。 我们已经知道App启动时,去哪里加载js代码的规则。有些时候,我们想在在模拟器上直接运行我们的App,但是我们又想启动Packager服务。那么我们就可以使用这个变量来让我们在编译App时,也需要将js代码打包到模拟器的.app包内。就可以达到这样的目的了 设置的方法如同前面介绍的方法一样。只需要将 export FORCE_BUNDLING=true

代码放入到iOS项目执行打包脚本的入口即可。


以上所述就是小编给大家介绍的《React Native启动脚本解读、优化》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Web Development Recipes

Web Development Recipes

Brian P. Hogan、Chris Warren、Mike Weber、Chris Johnson、Aaron Godin / Pragmatic Bookshelf / 2012-1-22 / USD 35.00

You'll see a full spectrum of cutting-edge web development techniques, from UI and eye candy recipes to solutions for data analysis, testing, and web hosting. Make buttons and content stand out with s......一起来看看 《Web Development Recipes》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具