一似袁、搭建環(huán)境及運行第一個項目
1.按照安裝教程中使用Homebrew一步一步的安裝RN的開發(fā)環(huán)境及運行環(huán)境。
2.按照教程創(chuàng)建第一個RN項目
3.使用命令行運行
react-native run-ios
4.加載資源.....等了很久勿决,無任何報錯愿棋。。诈嘿。一臉懵逼堪旧。
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è)務實現(xiàn)代碼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 部署工具的核心艘策,主要實現(xiàn)在 react-native/packager
里,在這個生成靜態(tài) bundle
的流程里渊季,主要做的事情是:
- 編譯/解析依賴
現(xiàn)代前端工程中朋蔫,編譯幾乎已經是必須的了,這里編譯主要做兩件事:ES6
-> 通用JS
却汉,JSX -> JS
驯妄。
RN 源碼以及業(yè)務代碼都是以 ES6 的語法去寫,像import xxx
這種寫法在不支持ES6
語法的JS
引擎上是無法運行的合砂,需要編譯成require('xxx')
青扔。此外像JSX
這種在 JS 代碼里嵌入XML
標簽的語法糖也需要編譯成普通JS
語法才能在JS
引擎上運行,所以需要一個編譯的過程既穆。此外需要把 JS 文件的依賴也解析出來赎懦,因為這涉及到對 JS 代碼的解析,把require('xxx')
語句解析出來幻工,所以這部分也是在編譯過程中處理励两。
這里統(tǒng)一用 Babel 這個庫去做所有編譯的工作。它的官網(wǎng)也說得很清楚它做了什么工作囊颅,除了編譯当悔,后續(xù)會提到的SourceMap
也是用它生成,由packager/src/JSTransformer
去封裝編譯解析后的數(shù)據(jù)踢代。
解析依賴是在packager/src/JSTransformer/worker/extract-dependencies.js
盲憎,這里用 babel解析出當前文件中require
的內容后組裝返回。編譯是在packager/src/JSTransformer/worker/worker.js
里胳挎。 - 管理依賴饼疙、打包壓縮
上述解析依賴僅提取了當前 JS 文件依賴的文件名,并沒有做依賴文件查找/讀取/拼裝/更新等工作慕爬,這個工作在packager/src/node-haste
里做窑眯,把一個個 JS 文件封裝成一個個Module
,根據(jù)上述解析出來的依賴信息医窿,去讀取依賴文件磅甩,并遞歸檢測依賴,直到所有依賴都加載完畢姥卢。
這里面還有層層處理卷要,最終所有依賴模塊會封裝成一個packager/src/Bundler
渣聚,提供給 cli 命令行調用,打包壓縮是小意思僧叉,在local-cli/bundle.js
里處理了奕枝。 - 請求執(zhí)行
在本地靜態(tài)bundle
模式下,RN 最終會統(tǒng)一執(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 命令 – 編譯文件 – 解析依賴 – 組裝數(shù)據(jù) – 寫入文件苍息,這個過程在代碼中實現(xiàn)流程很長,這里就不列出來了壹置,大致涉及的幾個文件的作用列以下:
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。