ReactNative 知識小集(1)-深入理解 React Native Debugging
翻譯改編自 Shaheen Ghiassy的Deep Diving React Native Debugging from medium.com
中文譯名: 深入理解 React Native Debugging
譯者注: 使用React Native(以下簡稱RN) Debug更是每天都會用到的技能.最近閑來看看一些原理性的文章,決定先從debug入手,可以更深入了解到框架背後的東西.原作者寫這篇文章的時候RN還是0.11版本,我的開發版本是 0.47.其實debug的原理沒有改變,只是個別環節的實現function增加了一些參數.下面我們開始正文
如果你和我一樣,當你看到React Native 簡單的debugging的時候,你一定會覺得這太拽了.在Chrome瀏覽器里打下斷點,就可以終端iOS應用.而且可以console和watch各種變數.
但如果你和我一樣,你一定不會相信編程中有魔法的存在,而要去探究他的工程實現.這篇文章就是帶你理解React Native Debugging 背後的原理.
tl;dr 文章很長,不喜勿讀
三方清單
- 一個NodeJS server. 稱為 Packager. 運行在 terminal中
- Chrome 瀏覽器從Packager載入你的React Native JavaScript,並提供內置的debugging 支持,就像 debug 普通的JavaScript代碼一樣
- 設備上跑的App 和 在瀏覽器中跑的js應用 通過webSocket 通信. 設備和瀏覽器互相通過JSON命令通訊,瀏覽器來控制設備. 當然站在瀏覽器背後的是這個 Packager.
下面就來詳細講述這三方的關係.
深入理解
如果你想繼續了解,你會對和debugging有關的過程,文件和操作更感興趣.
Note: React Native 發展迅速, 本篇文章基於 React Native 0.47 版本
讓我們先來直觀的感受一下上面提到的三方+你的代碼
* 一個典型的react應用中的各個角色
這四方之前的關係圖有點小複雜,我們會慢慢展開.Facebook和一些第三方都在致力於開發一站式的開發工具,例如[Nuclide IDE](Nuclide), [Deco Sofeware](Deco - React Native IDE),但無論怎麼說,我們還是探究一下各方的細節.
A. 你的移動設備: native code 運行在上面
B. NodeJS Server: Facebook』s [Packager](https://github.com/facebook/react-native/tree/master/packager) NodeJS Server. 一個類WebPack的項目,有個基於 CommonJS 的模塊系統, 和一堆可用的黑科技.用來幫助 React Native 開發
C. Chrome 瀏覽器: 這個不用介紹了吧
D. React Native JavaScript code: 你的價值體現
那麼究竟在 Debugging 時發生了什麼
Step 1: Start Packager
如果你在命令行中輸入: 「npm start」. 那麼他會觸發 react-native/local-cli/cli.js 去執行start.這個在 項目的package.json 中可以看到. 包括在XCode > Build Phases > React-Native shell > react-native-xcode.sh 最後執行的也是 cli.js. 然後他會起一個 NodeJS Server,基於 Connect 開發的.
runServer.js順序是這樣的
- Packager 打包好並host http://localhost:8081/index.ios.bundle
- packager 觸發 http://localhost:8081/launch-chrome-devtools
- chrome打開 http://localhost:8081/debugger-ui.html
[Terminal 中的呈現]
同時 Packager 會配置一個 WebSocket 的服務,這個稍後介紹.
tips: webSocket 是附著在http協議上的. http://localhost:8081 ws://localhost:8081
Step 2: Run React Native App in Simulator
在XCode中 run,然後模擬器中會呈現. 實測是這樣的, 跳過Step 1.直接在Xcode中run,系統會自動默認的Terminal,然後跑 npm start. 在真機調試一樣也會啟動.
真機調試 Tip: 在用真機的時候,ws 服務的地址要換成 筆記本的IP,不能用localhost
要不iOS設備在Wifi環境下訪問不到 ws服務.
Automated IP Configuration for React Native Development - Modus Create
一旦App在設備上跑起來,他會去訪問 http://localhost.com:8081/index.ios.bundle. 並把它轉換成原生的代碼執行. 想看React Native原理可以參考
React Native通信機制詳解React Native之源碼分析Packager 是跑在本地的,一旦他收到瀏覽器或者App的請求,他會使用Babel和Facebook自家的grapher 去collect,concat,transplie and modularize,你的 React Native JavaScript (D) 代碼編程一個 response. 所以當你改動代碼並保存的時候你會看到 Terminal 中會重新 transforming.
訪問 http://localhost:8081/index.ios.bundle, 看一下package之後的代碼
當iOS設備收到代碼的時候, 它會把JavaScript 代碼放到 Apple』s JavaScript Code 中去執行.
Step 3: Turn on Debugging Mode
現在App在設備上正常的跑著,下一步就是進入 debug 模式,這樣就可以通過Chrome的webSocket inspector 去查看 React Native 編譯過的版本 和 未編譯版本 之前傳輸的數據了.
在模擬器上按 Command+D.會彈出調試選項.真機調試的時候搖一搖也會出現
當你點擊Debug in Chrome 的時候,客戶端會發起一個請求 http://localhost:8081/launch-chrome-devtools.然後Packager會接到這個請求,然後通過 sindresorhus/opn 執行兩個操作.
第一: 打開Chrome 的一個tab
第二: 讓這個tab打開URL http://localhost:8081/debugger-ui
Step 4: Debugger-ui.html and Device establish a connection
現在我們有了一個Chrome Tab,url 是 http://localhost:8081/debugger-ui 這個頁面的靜態文件是debugger-ui.html.這個網頁會有一個請求里通過. websocket 和 Packager 保持通訊.
https://gist.github.com/mactive/47fd28ec51037026ae4364cdcf633cbb你的App會發送3個WebSocket ping到你的Chrome. 如果Chrome反饋給你 expectedId/sessionId,那麼鏈接將會馬上建立.否則設備會拋出一個紅屏」Runtime is not ready」.相信做過React Native 開發的對這個紅屏不會陌生.一般 Command+R 一下就可以解決.你的App和瀏覽器之前的交互中間都會經過 NodeJSServer.因為http和ws服務都是他運行的.
Step 5: Execute Application Script
鏈接建立之後.App將會發送 WebSocket 消息到瀏覽器,去載入 JavaScript code.
https://gist.github.com/mactive/3cb2e2c53c3b9edf48a0793dae3c82cf發送的這個消息非常有意思,發送的時候回注入一個字典.字典中有很多 module/method/id 的映射關係.這裡的module/method 就對應RCTModule 和 RCTMethod. 這個字典在chrome中也有體現(下圖).一個真實項目中的字典可以參見附錄A
這個啟動消息被運行在Chrome的debugger-ui接受到.消息被插接之後放入Chrome的window對象的「__fbBatchedBridgeConfig」屬性中.
[chrome 中的 window.__fbBatchedBridgeConfig]
https://gist.github.com/mactive/c17a2dca25b3711e32c713a9f0006e9b當Chrome的window對象更新完之後.瀏覽器會載入 bundlejs.
然後debuggerWorker 會吧bundlejs+sourcemap 再還原成一個一個的文件供我們debug,在chrome中可以用 Command+P 來定位文件
- bundlejs 後的文件: http://localhost:8081/index.ios.bundle
- sourcemap: http://localhost:8081/index.ios.map?platform=ios&dev=true
從上圖可以看出,App和Chrome之間的交互離不開Packager,最後Chrome中的網頁會告知executeApplicationScript webSocket消息已經發出
Step 6: Run
當 executeApplicationScript 被設備成功接受到之後.App就會重新載入js,呈現給我們.感覺Chrome和App之間通過WebSocket通信,屌屌的. 至此你可以在Chrome像調試網頁的JS一樣調試ReactNative的js了.不過請注意很多第三方node_module的東西無法console出來.就算是在下斷點的時候.比如lodash,具體原因不清.
結語
令人值得稱道的一點是, React Native js在Chrome中的運行方式和 React Native js在設備上 JavaScriptCore 的運行方式很像.都是有個 Module和method的巨大的映射表.
例如 window.__fbBatchedBridgeConfig 在設備上也有類似的mapping.這個相似點很棒.這給我們提供可一種觀察JavaScriptCore的方案.
希望本文能夠幫你更好的理解 React Native, 並寫出更好的應用.
附錄和其他
- 一份完整的符號表
- 使用VSCode, 在編輯器中Debug ReactNativeApp
- 開啟MessageQueue.spy()
import MessageQueue from react-native/Libraries/BatchedBridge/MessageQueue
MessageQueue.spy(true);
接下來會繼續翻譯和撰寫更多的原理方面的文章.
推薦閱讀:
※國內有哪些上線的react-native作品?
※React、Angular和Vue.js三者中哪一個更易學,更容易理解?
※如何評價 React Native Android?
※移動應用開發入門,是否可以考慮先學習 React Native?
※react native 真機調試apk無法安裝到設備?
TAG:ReactNative | 軟體調試 |