React-Native学习笔记之第一个RN项目

一、搭建环境及运行第一个项目

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

React-Native学习笔记之第一个RN项目_第1张图片
Snip20170822_1.png

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

React/RCTbundleURLProvider.h not found未找到文件。
7.检查RN版本 react-native --version 0.47.2
版本过高,boost下载未成功。
8.使用命令: react-native init MyApp --version 0.44.3创建指定版本的App
9.编译程序不报错。
10.使用Xcode查看RN项目结构:
React-Native学习笔记之第一个RN项目_第2张图片
Snip20170822_4.png

11.继续输入命令: react-native run-ios运行成功。
12.运行结果
React-Native学习笔记之第一个RN项目_第3张图片
Snip20170822_3.png

13.找到 index.ios.js文件使用sublime打开
React-Native学习笔记之第一个RN项目_第4张图片
Snip20170822_8.png

14.编辑内容后继续使用命令: react-native run-ios运行结果如下:

React-Native学习笔记之第一个RN项目_第5张图片
Snip20170822_6.png

React-Native学习笔记之第一个RN项目_第6张图片
Snip20170822_2.png

第一个项目 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 版,或无法连接本地 ServeriPhone 真机上的 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 的流程里,主要做的事情是:

  1. 编译/解析依赖
    现代前端工程中,编译几乎已经是必须的了,这里编译主要做两件事:ES6 -> 通用JSJSX -> 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
    里。
  2. 管理依赖、打包压缩
    上述解析依赖仅提取了当前 JS 文件依赖的文件名,并没有做依赖文件查找/读取/拼装/更新等工作,这个工作在 packager/src/node-haste里做,把一个个 JS 文件封装成一个个Module,根据上述解析出来的依赖信息,去读取依赖文件,并递归检测依赖,直到所有依赖都加载完毕。
    这里面还有层层处理,最终所有依赖模块会封装成一个 packager/src/Bundler,提供给 cli 命令行调用,打包压缩是小意思,在local-cli/bundle.js里处理了。
  3. 请求执行
    在本地静态 bundle 模式下,RN 最终会统一执行上述生成的 main.bundle,所有 JS 代码都在这里面,由 RCTBundleURLProvider.m处理执行,整个 RN 应用就跑起来了。
    main.bundle 里是合并后的 JS 代码,如果想要看这个 JS 文件合并之前是包括哪些 JS 源文件,可以在上述模块组装的过程中去打出每个模块的信息,例如在 packager/src/Bundler/Bundle.jsaddModule()
    方法里加上 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。

你可能感兴趣的:(React-Native学习笔记之第一个RN项目)