React-Native學習筆記之第一個RN項目

一、搭建環(huán)境及運行第一個項目

1.按照安裝教程中使用Homebrew一步一步的安裝RN的開發(fā)環(huán)境及運行環(huán)境。
2.按照教程創(chuàng)建第一個RN項目

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創(chuàng)建指定版本的App
9.編譯程序不報錯。
10.使用Xcode查看RN項目結構:
Snip20170822_4.png

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

13.找到index.ios.js文件使用sublime打開
Snip20170822_8.png

14.編輯內容后繼續(xù)使用命令:react-native run-ios運行結果如下:

Snip20170822_6.png

Snip20170822_2.png

第一個項目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 版,或無法連接本地 ServeriPhone 真機上的 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 的流程里,主要做的事情是:

  1. 編譯/解析依賴
    現代前端工程中,編譯幾乎已經是必須的了,這里編譯主要做兩件事: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
    里。
  2. 管理依賴、打包壓縮
    上述解析依賴僅提取了當前 JS 文件依賴的文件名,并沒有做依賴文件查找/讀取/拼裝/更新等工作,這個工作在 packager/src/node-haste里做,把一個個 JS 文件封裝成一個個Module,根據上述解析出來的依賴信息,去讀取依賴文件,并遞歸檢測依賴,直到所有依賴都加載完畢。
    這里面還有層層處理,最終所有依賴模塊會封裝成一個 packager/src/Bundler,提供給 cli 命令行調用,打包壓縮是小意思,在local-cli/bundle.js里處理了。
  3. 請求執(zhí)行
    在本地靜態(tài) bundle 模式下,RN 最終會統一執(zhí)行上述生成的 main.bundle,所有 JS 代碼都在這里面,由 RCTBundleURLProvider.m處理執(zhí)行,整個 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。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容