一、搭建环境及运行第一个项目
1.按照安装教程中使用Homebrew一步一步的安装RN的开发环境及运行环境。
2.按照教程创建第一个RN项目

3.使用命令行运行
react-native run-ios4.加载资源.....等了很久,无任何报错。。。一脸懵逼。
5.使用Xcode打开iOS文件夹下的工程文件。
6.报错信息如下:

React/RCTbundleURLProvider.h not found未找到文件。7.检查RN版本
react-native --version 0.47.2版本过高,boost下载未成功。
8.使用命令:
react-native init MyApp --version 0.44.3创建指定版本的App9.编译程序不报错。
10.使用Xcode查看RN项目结构:

11.继续输入命令:
react-native run-ios运行成功。12.运行结果

13.找到
index.ios.js文件使用sublime打开
14.编辑内容后继续使用命令:
react-native run-ios运行结果如下:


第一个项目
Hello World了已经。
二、思考第一个问题:react-native init AwesomeProject 这个命令做了什么,是怎样创建 RN 模板项目的?
实际上,在按照教程安装环境后,会在/usr/local/bin/加上react-native脚本,实际是个node.js脚本,也就是github上的react-native-cli/index.js,在命令行全局调用react-native就会调用这个脚本。这个文件的注释也可以看到,这只是一个转接层,所有命令都会转接到local-cli上,但很奇怪react-native init创建工程的逻辑部分在这个转接层react-native-cli/index.js,部分在 local-cli/init/init.js,其他命令则全部转接到 local-cli上。
看看执行 react-native init AwesomeProject 的流程:
- 安装
react-native依赖:在AwesomeProject目录执行npm install react-native,安装react-native所有依赖的node模块。这是init命令第一个做的事情,代码在react-native-cli/index.js -> run(),复制项目模板:安装依赖后init命令随即转接到local-cli,通过local-cli/generator初始化项目,复制项目模板,模板文件在local-cli/templates里。 - 链接
native代码源文件:项目模板复制后需要把刚才安装的node_module/react-native里的源文件链接到natvie工程上,不同平台有不同逻辑,都在local-cli/link里处理native工程的链接。iOS 处理逻辑在local-cli/link/ios/。
这一步骤处理后,AwesomeProject.xcodeproj所需要的模块都链接完成,可以直接运行,可以看到工程Libraries里所有模块都是从AwesomeProject/node_modules/react-native/里链接过来的。
react-native模块依赖了 500 多个npm模块,这在前端界也算是正常,这些模块小部分是 RN 源码依赖的 JS 模块,大部分是用于前端构建,包括 JS 编译/打包/语法检测/http服务中间层等。
RN 模板项目创建过程大致就是这样。
项目 JS 源码在哪里,如何跑起来的?
在生成的AwesomeProject模板项目里,iOS 端所依赖的所有模块和源码直接可以在工程里看到。但 JS 端的源码在项目里只看到业务实现代码index.ios.js,XCode 项目跑起来后,index.ios.js就执行生效了,RN 核心 JS 代码在哪里,有哪些,怎么跑起来的,都是个黑盒,接下来拆解下,看看 JS 代码是怎样运行起来的。
两种模式
RN 在 iOS 上对 JS 脚本的处理分两种模式:
- 本地
Server模式。在本地自建一个Server,客户端通过请求的方式获取 JS 代码。对于在模拟器跑debug版,会使用这种方式,用于接入 chrome 调试和脚本实时更新。 - 本地静态
bundle模式。编译时就把所有相关 JS 文件打包编译到 APP 里,运行时直接本地读取。对于所有release版,或无法连接本地Server的iPhone真机上的debug版,会使用这种方式。
本地 Server 模式在下一节 chrome 调试再描述,这里先看看本地静态 bundle 模式。
本地静态 bundle
在本地静态Bundle模式中,最终所有 JS 代码都会打包成一个文件,客户端最终只需读取一个打包后的 JS 文件执行。这里从依赖分散的 JS 源文件,到最终可执行的单个 JS,有一个编译和打包 JS 的处理过程。这套处理过程的启动是在主工程AwesomeProject.xcodeproj Build Phases里执行了一个脚本node_modules/react-native/packager/react-native-xcode.sh
,最终它在 Release版或真机上执行了这样一条打包命令:
react-native bundle --entry-file index.ios.js --platform ios --dev true --reset-cache --bundle-output main.bundle --assets-dest assets
这个命令最终会输出一个 main.bundle文件,实际是个 JS 文件,包含了 RN 所有核心代码和我们项目的业务代码(这里只有index.ios.js)。
这个打包命令包含非常多处理,流程很长,算是整个 RN 部署工具的核心,主要实现在 react-native/packager里,在这个生成静态 bundle 的流程里,主要做的事情是:
- 编译/解析依赖
现代前端工程中,编译几乎已经是必须的了,这里编译主要做两件事:ES6-> 通用JS,JSX -> JS。
RN 源码以及业务代码都是以 ES6 的语法去写,像import xxx这种写法在不支持ES6语法的JS引擎上是无法运行的,需要编译成require('xxx')。此外像JSX这种在 JS 代码里嵌入XML标签的语法糖也需要编译成普通JS语法才能在JS引擎上运行,所以需要一个编译的过程。此外需要把 JS 文件的依赖也解析出来,因为这涉及到对 JS 代码的解析,把require('xxx')语句解析出来,所以这部分也是在编译过程中处理。
这里统一用 Babel 这个库去做所有编译的工作。它的官网也说得很清楚它做了什么工作,除了编译,后续会提到的SourceMap也是用它生成,由packager/src/JSTransformer去封装编译解析后的数据。
解析依赖是在packager/src/JSTransformer/worker/extract-dependencies.js,这里用 babel解析出当前文件中require的内容后组装返回。编译是在packager/src/JSTransformer/worker/worker.js
里。 - 管理依赖、打包压缩
上述解析依赖仅提取了当前 JS 文件依赖的文件名,并没有做依赖文件查找/读取/拼装/更新等工作,这个工作在packager/src/node-haste里做,把一个个 JS 文件封装成一个个Module,根据上述解析出来的依赖信息,去读取依赖文件,并递归检测依赖,直到所有依赖都加载完毕。
这里面还有层层处理,最终所有依赖模块会封装成一个packager/src/Bundler,提供给 cli 命令行调用,打包压缩是小意思,在local-cli/bundle.js里处理了。 - 请求执行
在本地静态bundle模式下,RN 最终会统一执行上述生成的main.bundle,所有 JS 代码都在这里面,由RCTBundleURLProvider.m处理执行,整个 RN 应用就跑起来了。
main.bundle里是合并后的 JS 代码,如果想要看这个 JS 文件合并之前是包括哪些 JS 源文件,可以在上述模块组装的过程中去打出每个模块的信息,例如在packager/src/Bundler/Bundle.js的addModule()
方法里加上console.log(moduleTransport.sourcePath)就能看到所有依赖的 JS 文件路径。另外通过下述SourceMap能更方便地看到。
代码流程
从 cli 命令 – 编译文件 – 解析依赖 – 组装数据 – 写入文件,这个过程在代码中实现流程很长,这里就不列出来了,大致涉及的几个文件的作用列以下:
local-cli/bundle/ - cli命令入口,传参,获取组装好的 Bundle压缩/写入文件
packager/src/Bundler/Bundle.js - 保存 bundle相关的所有模块信息/依赖/源码
packager/src/Bundler/index.js - 组装 Bundle 对象packager/src/JSTransformer - babel 转接,编译 JS,解析依赖
packager/src/node-haste - 管理依赖 cache,把 JS 源文件模块封装成 Module 对象
packager/src/Resolver - JS 模块组装打包成一个文件并不只是直接把 JS 源码拼一起,还需要重新封装模块,处理引用逻辑
第二部分文字引自bang's blog。