React Native 网络层分析
文:志俊(沪江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浏览器中,FetchAPI提供了和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调试网络只是他的一个功能之一,其他还有很多强大的功能。有兴趣可以查看他的文档。
使用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 https://github.com/facebook/react-native/issues/934
具体实现在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实战项目教学(含视频)| 课程大纲介绍
React Native 网络层分析的更多相关文章
- Hybrid APP基础篇(二)->Native、Hybrid、React Native、Web App方案的分析比较
说明 Native.Hybrid.React.Web App方案的分析比较 目录 前言 参考来源 前置技术要求 楔子 几种APP开发模式 概述 Native App Web App Hybrid Ap ...
- React Native 从入门到原理
React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原理的文章却寥寥无几. 本文分为两个部分:上半部分用通 ...
- 关于React Native 火热的话题,从入门到原理
本文授权转载,作者:bestswifter(简书) React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原 ...
- React Native 从入门到原理一
React Native 从入门到原理一 React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原理的文章却 ...
- React Native 入门到原理(详解)
抛砖引玉(帮你更好的去理解怎么产生的 能做什么) 砖一.动态配置 由于 AppStore 审核周期的限制,如何动态的更改 app 成为了永恒的话题.无论采用何种方式,我们的流程总是可以归结为以下三部曲 ...
- React Native分析(index.ios.js)
定义创建组件MyComponent(index.ios.js): 'use strict' var React = require('react-native'); var { AppRegistry ...
- React Native 4 for Android源码分析 一《JNI智能指针之介绍篇》
文/ Tamic: http://blog.csdn.net/sk719887916/article/details/53455441 原文:http://blog.csdn.net/eewolf/a ...
- H5、React Native、Native应用对比分析
每日更新关注:http://weibo.com/hanjunqiang 新浪微博!iOS开发者交流QQ群: 446310206 "存在即合理".凡是存在的,都是合乎规律的.任何新 ...
- React Native超简单完整示例-tabs、页面导航、热更新、用户行为分析
初学React Native,如果没有人指引,会发现好多东西无从下手,但当有人指引后,会发现其实很简单.这也是本人写这篇博客的主要原因,希望能帮到初学者. 本文不会介绍如何搭建开发环境,如果你还没有搭 ...
随机推荐
- 在用python操作mysql时报错:ModuleNotFoundError: No module named 'MySQLdb'
用Flask+python+mysql写一个小项目 系统 win10 py版本:3.6.1 在配置数据库时报错ModuleNotFoundError: No module named 'MySQLdb ...
- 《程序设计方法》【PDF】下载
内容简介 <程序设计方法>主要以方法为主导,结合C语言,把程序设计方法学研究中若干成熟的理论和方法用通俗易懂的语言描述出来.<程序设计方法>还选取趣味性强.技巧性高.能够启发学 ...
- Mybatis篇总结
本文是对慕课网上"搞定SSM开发"路径的系列课程的总结,详细的项目文档和课程总结放在github上了.点击查看 JDBC写法 //sql: String sql = "s ...
- Python的HTTP服务实例
1.前言 今天需要实现一个Pyhton的http服务,与Web的JS进行交换. 2.实例代码 支持HEAD.GET.POST方法,将参数转换为JSON格式,返回结果以JSON字符串返回. import ...
- Mysql数据库重要知识点(知了堂学习心得)
Mysql数据库知识点 1.管理数据库语句: 使用数据库: use test; 添加数据库: create database 数据库名; create database test; 修改数据库: al ...
- 《Django By Example》Chap 4中出现的 “RelatedObjectDoesNotExist”错误
models.py
- 如何减轻ajax定时触发对服务器造成的压力和带宽的压力?ajax-长轮训
AJAX长轮询的方法来解决频繁对后台的请求,进一步减小压力 在实现过程发现AJAX的多次请求会出现多线程并发的问题又使用线程同步来解决该问题 个人对ajax长轮询的一点愚见 ajax请示后台时,后台程 ...
- oracle自动备份_expdp_Linux
[oracle@hbsjxtdb1 ~]$ crontab -e 0 4 * * * /backup/script/backupexpdp.sh [oracle@hbsjxtdb1 ~]$ cront ...
- Spark监控官方文档学习笔记
任务的监控和使用 有几种方式监控spark应用:Web UI,指标和外部方法 Web接口 每个SparkContext都会启动一个web UI,默认是4040端口,用来展示一些信息: 一系列调度的st ...
- JavaScript的DOM编程--08--复习
<html> <head> <meta http-equiv="Content-Type" content="text/html; char ...