文:志俊(滬江Web前端)
本文原創,轉載請註明作者及出處
在使用React Native
開發中,我們熟練的採用JavaScript
的方式傳送請求的方式傳送一個請求到服務端,但是處理這個請求的過程其實和處理Web
應用中傳送的請求的過程是不一樣的。因為處理這個請求的目標不是瀏覽器,而是嵌入這個應用的原生作業系統。
在處理React Native
的請求時,分為兩部分:一部分是JavaScript
的執行環境,另一部分是嵌入JavaScript
的Native
(即原生Android
和IOS
)執行環境。React Native
內建了三種傳送網路請求的方式:fetch
, XMLHttpRequest
和 WebSocket
。但是React Native
的執行環境和Web應用的執行環境不一樣,所以需要在原生應用層採用自定義函式來擴充執行時(runtime)環境來處理JavaScript
發出的網路請求。
請求傳送方式及過程
對於常用的網路請求物件:XMLHttpRequest(XHR)、Fetch及WebSocket,熟悉前端開發的同學應該非常瞭解。XHR
是Web開發中用得比較多的傳送請求的方式,Fetch
和Websocket
也是後起之秀,在很多現代Web應用中得以採用。但是,在React Native
中,這些物件的使用和Web應用是有差別的。當你在JS層呼叫網路請求時,其實是經歷了兩個過程才到達真正的伺服器端。就像頭部banner表示的那樣。
XMLHttpRequest(XHR)
在React Native
中, XMLHttpRequest(XHR)
由兩部分組成: “前端”(front-end)和“後端”(back-end)。前端負責與JavaScript
互動,後端負責在原生平臺上轉換JavaScript
傳送過來的請求為原生系統自己的請求。
這裡的後端其實是一個原生平臺頂層抽象的統一API層,使得JavaScript
層可以呼叫原先系統的網路模組。例如IOS
下內建的URLSession模組和Android
下的OKHTTP模組。
Fetch
在現代Web瀏覽器中,Fetch
API提供了和XHR
大部分相同的功能,但是Fetch
提供了一種更加簡單,高效的方式來跨網路非同步獲取資源,同時可操縱Request
和Response
物件來複用請求。
但是在React Native
中,為了相容兩種平臺的差異,採用了依賴於XMLHttpRequest
的Fetch Polyfill來實現這個請求物件。這就意味著我們不能像實用Web平臺下的Fetch
物件一樣來實用React Native
下的該物件。比如採用這個物件來傳送binary資料。當然可以採用第三方的庫比如react-native-fetch-blob來實現相應的功能。
Websocket
Websocket
作為一種新的通訊協議,採用全雙工通訊方式與伺服器間進行通訊的網路技術。
在React Native
中,Websocket
並不是一個獨立的請求,和XMLHttpRequest(XHR)
一樣由兩部分組成: “前端”(front-end)和“後端”(back-end)。前端負責與JavaScript
互動,後端負責在原生平臺上轉換JavaScript
傳送過來的請求為原生系統自己的請求。在IOS中採用的是自己開發的NSStream,而在Android系統中則是OKHTTP模組。
檢視React Native中的網路請求
在React Native
開發中,你可以通過Chrome Developer Tools (CDT)
的Sources
皮膚中除錯javascript
部分的程式碼,包括斷點、輸出資訊、斷點除錯等一切javascript
除錯所需的資訊。但是,唯一缺少的就是網路請求的跟蹤除錯。我們沒辦法像Web開發那樣,可以通過CDT
中的網路皮膚(Network
)來檢視應用的網路請求的相關資訊。
使用代理除錯網路請求
雖然沒有辦法通過CDT
檢視應用的網路請求,但是我們可以通過Fiddler
、CharlesProxy
及Wireshark
等軟體設定代理,來檢視追蹤除錯網路請求。這裡使用Fiddler
來作為代理。
-
首先設定
Fiddler
的代理埠: 開啟Filddler -> Tool -> Options -> Connects,在監聽埠處填寫相應的埠號, -
在除錯機器上、
Android
或者IOS
模擬器模擬器中設定代理: 找到除錯的機器上的網路設定中,設定當前連線的WIFI的代理地址 -
重新整理應用,在
fiddler
中檢視網路請求(提示:右鍵,在新頁籤中開啟可檢視清晰圖片):
在代理應用中,我們可以檢視請求頭,返回頭,返回結果等相關的網路資訊。當然,還可以根據相關代理軟體攔截請求,重新設定後傳送。
使用Reactotron除錯網路
上面通過設定代理的方式來檢視和追蹤網路請求,雖然功能強大,但是實際操作起來有些難度,上手成本比較高。通過使用Reactotron
,可以將除錯的配置資訊整合到應用中,方便在不同的開發環境下有相同的除錯配置,節約開發配置成本。
Reactotron
由兩部分組成,一部分是除錯應用,一部分是除錯配置。
- 除錯應用分別有各個作業系統的GUI安裝版本。當然,如果習慣使用命令列,也可以使用
NPM
安裝reactotron-cli
npm install -g reactotron-cli
複製程式碼
- 設定除錯配置:
在你的`React Native`應用中安裝`reactotron-react-native`
```
npm i --save-dev reactotron-react-native
```
然後,在你的應用的新增配置檔案,定製除錯內容:
```
import Reactotron, {
trackGlobalErrors,
openInEditor,
overlay,
asyncStorage,
networking
} from 'reactotron-react-native'
Reactotron
.configure({
name: 'xxx' // 除錯的名稱
})
.use(trackGlobalErrors()) // 設定監聽全域性錯誤
.use(openInEditor()) // 設定在編輯器中開啟錯誤
.use(overlay()) // 設定圖片遮蓋圖片(用於UI還原度對比)
.use(asyncStorage()) // 設定非同步儲存除錯
.use(networking()) // 設定網路除錯
.connect() // 連線應用(必須)
```
然後在你的應用的入口檔案中引入這個配置檔案。然後重新啟動應用。當然,還可以使用正規表示式過濾請求的`contentType`的型別和要忽略的請求的`url`,見下面的配置:
```
.use(networking({
ignoreContentTypes: /^(image)\/.*$/i, // 設定reactotron要忽略的檔案型別
ignoreUrls: /\/(logs|symbolicate)$/, // 設定reactotron要忽略的url請求路徑
}))
```
![reactotron設定](https://raw.githubusercontent.com/yandan66/blogs/master/source/_posts/20171124/reactotron_02.png)
![reactotron設定](https://raw.githubusercontent.com/yandan66/blogs/master/source/_posts/20171124/reactotron_03.png)
![reactotron結果](https://gitlab.yeshj.com/hujiang-f2e/fun-book/raw/5f6236238c0e51a085d3be32b349436c5e3dad73/%E9%97%A8%E6%88%B7%E7%BA%BF/image/ezgif.com-resize.gif)
複製程式碼
reactotron
除錯網路只是他的一個功能之一,其他還有很多強大的功能。有興趣可以檢視他的文件。
使用Chrome Developer Tools網路皮膚除錯網路
React Native
預設暴露出來的介面中,是沒有直接在Chrome Developer Tools
檢視網路請求的方法的,檢視 RN 原始碼 Libraries/Core/InitializeCore.js,註釋中寫著:
Sets an object’s property. If a property with the same name exists, this will replace it but maintain its descriptor configuration. By default, the property will replaced with a lazy getter. * The original property value will be preserved as original[PropertyName] so that, if necessary, it can be restored. For example, if you want to route network requests through DevTools (to trace them): * global.XMLHttpRequest = global.originalXMLHttpRequest; * @see github.com/facebook/re…
具體實現在XHRInterceptor.js中。原來的XMLHttpRequest
被改寫成了 originalXMLHttpRequest
,所以要在Chrome
中顯示network
只需要替換XMLHttpRequest
為 originalXMLHttpRequest
。在入口檔案處設定:
if (__DEV__) {
GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest
}
複製程式碼
當然,這樣有可能會產生CORS
, Chrome
會限制跨域請求。這時要麼後端配合一下去除限制,要麼使用 Allow-Control-Allow-Origin: * 外掛。
React Native傳送二進位制資料(binary data )
由於React Native
中Fetch
物件的底層採用的是XHR
實現,這就限制了傳送二進位制資料的功能。當然React Native
提供了一系列的方式來解決這個問題,比如: 轉換二進位制檔案為base64字串或者採用第三方庫 react-native-fetch-blob。但是並沒有從底層解決這個問題。
轉換二進位制為base64傳送
到目前為止,React Native
不能傳送非序列化的資料,所以,要傳送二進位制資料,採用Base64編碼的字串是個不錯的選擇。
例如,你從伺服器下載一張圖片(注意:不是通過url
從伺服器獲取),請求通過JavaScript執行緒,再通過React Native
提供的橋接器,最後通過原生系統的網路模組傳送到服務端。服務端返回一個Base64編碼過的圖片,JavaScript
執行緒收到返回的字串後,會分配相應的記憶體,然後React Native
會呼叫相應的原生模組渲染成相應圖片。但是值得主要的是,這種方式會造成典型的效能問題——記憶體洩漏。
通過Base64編碼的方式傳輸二進位制檔案,這裡會造成一系列效能問題,這篇文章中列出了大部分效能問題及提出了相應的解決方案。
現在使用的各種方法傳送二進位制檔案都存在各種問題,最終的解決方式是要相應的標準能夠實現二進位制的傳輸。目前,WebSocket
已經支援了二進位制傳輸。在最新版本的React Native
層也已經支援WebSocket
協議來傳輸二進位制檔案,但是,相應的原生平臺的網路模組暫時還不支援。
總結
React Native
開發方式是非常不錯的體驗,但是,受各個平臺差異和標準的限制,不得不折中處理一些問題。隨之而來的是相應的效能、效率的問題。另外,採用開發,效能上和使用者體驗上和原生應用還是有一定差距。但是如果在原生應用中能夠整合React Native
,會顯著提高開發效率。
參考
推薦: 翻譯專案Master的自述:
1. 乾貨|人人都是翻譯專案的Master
2. iKcamp出品微信小程式教學共5章16小節彙總(含視訊)
3. 開始免費連載啦~每週2更共11堂iKcamp課|基於Koa2搭建Node.js實戰專案教學(含視訊)| 課程大綱介紹
2019年,iKcamp原創新書《Koa與Node.js開發實戰》已在京東、天貓、亞馬遜、噹噹開售啦!