一、搭建環(huán)境及運行第一個項目
1.按照安裝教程中使用Homebrew一步一步的安裝RN的開發(fā)環(huán)境及運行環(huán)境。
2.按照教程創(chuàng)建第一個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創(chuàng)建指定版本的App9.編譯程序不報錯。
10.使用Xcode查看RN項目結構:

11.繼續(xù)輸入命令:
react-native run-ios運行成功。12.運行結果

13.找到
index.ios.js文件使用sublime打開
14.編輯內容后繼續(xù)使用命令:
react-native run-ios運行結果如下:


第一個項目
Hello World了已經。
二、思考第一個問題:react-native init AwesomeProject 這個命令做了什么,是怎樣創(chuàng)建 RN 模板項目的?
實際上,在按照教程安裝環(huán)境后,會在/usr/local/bin/加上react-native腳本,實際是個node.js腳本,也就是github上的react-native-cli/index.js,在命令行全局調用react-native就會調用這個腳本。這個文件的注釋也可以看到,這只是一個轉接層,所有命令都會轉接到local-cli上,但很奇怪react-native init創(chuàng)建工程的邏輯部分在這個轉接層react-native-cli/index.js,部分在 local-cli/init/init.js,其他命令則全部轉接到 local-cli上。
看看執(zhí)行 react-native init AwesomeProject 的流程:
- 安裝
react-native依賴:在AwesomeProject目錄執(zhí)行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 模板項目創(chuàng)建過程大致就是這樣。
項目 JS 源碼在哪里,如何跑起來的?
在生成的AwesomeProject模板項目里,iOS 端所依賴的所有模塊和源碼直接可以在工程里看到。但 JS 端的源碼在項目里只看到業(yè)務實現代碼index.ios.js,XCode 項目跑起來后,index.ios.js就執(zhí)行生效了,RN 核心 JS 代碼在哪里,有哪些,怎么跑起來的,都是個黑盒,接下來拆解下,看看 JS 代碼是怎樣運行起來的。
兩種模式
RN 在 iOS 上對 JS 腳本的處理分兩種模式:
- 本地
Server模式。在本地自建一個Server,客戶端通過請求的方式獲取 JS 代碼。對于在模擬器跑debug版,會使用這種方式,用于接入 chrome 調試和腳本實時更新。 - 本地靜態(tài)
bundle模式。編譯時就把所有相關 JS 文件打包編譯到 APP 里,運行時直接本地讀取。對于所有release版,或無法連接本地Server的iPhone真機上的debug版,會使用這種方式。
本地 Server 模式在下一節(jié) chrome 調試再描述,這里先看看本地靜態(tài) bundle 模式。
本地靜態(tài) bundle
在本地靜態(tài)Bundle模式中,最終所有 JS 代碼都會打包成一個文件,客戶端最終只需讀取一個打包后的 JS 文件執(zhí)行。這里從依賴分散的 JS 源文件,到最終可執(zhí)行的單個 JS,有一個編譯和打包 JS 的處理過程。這套處理過程的啟動是在主工程AwesomeProject.xcodeproj Build Phases里執(zhí)行了一個腳本node_modules/react-native/packager/react-native-xcode.sh
,最終它在 Release版或真機上執(zhí)行了這樣一條打包命令:
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 所有核心代碼和我們項目的業(yè)務代碼(這里只有index.ios.js)。
這個打包命令包含非常多處理,流程很長,算是整個 RN 部署工具的核心,主要實現在 react-native/packager里,在這個生成靜態(tài) bundle 的流程里,主要做的事情是:
- 編譯/解析依賴
現代前端工程中,編譯幾乎已經是必須的了,這里編譯主要做兩件事:ES6-> 通用JS,JSX -> JS。
RN 源碼以及業(yè)務代碼都是以 ES6 的語法去寫,像import xxx這種寫法在不支持ES6語法的JS引擎上是無法運行的,需要編譯成require('xxx')。此外像JSX這種在 JS 代碼里嵌入XML標簽的語法糖也需要編譯成普通JS語法才能在JS引擎上運行,所以需要一個編譯的過程。此外需要把 JS 文件的依賴也解析出來,因為這涉及到對 JS 代碼的解析,把require('xxx')語句解析出來,所以這部分也是在編譯過程中處理。
這里統一用 Babel 這個庫去做所有編譯的工作。它的官網也說得很清楚它做了什么工作,除了編譯,后續(xù)會提到的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里處理了。 - 請求執(zhí)行
在本地靜態(tài)bundle模式下,RN 最終會統一執(zhí)行上述生成的main.bundle,所有 JS 代碼都在這里面,由RCTBundleURLProvider.m處理執(zhí)行,整個 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。