文:志俊(沪江Web前端)

本文原创,转载请注明作者及出处

在使用React Native开发中,我们熟练的采用JavaScript的方式发送请求的方式发送一个请求到服务端,但是处理这个请求的过程其实和处理Web应用中发送的请求的过程是不一样的。因为处理这个请求的目标不是浏览器,而是嵌入这个应用的原生操作系统。

在处理React Native的请求时,分为两部分:一部分是JavaScript的运行环境,另一部分是嵌入JavaScriptNative(即原生AndroidIOS)运行环境。React Native内置了三种发送网络请求的方式:fetch, XMLHttpRequestWebSocket。但是React Native的运行环境和Web应用的运行环境不一样,所以需要在原生应用层采用自定义函数来拓展运行时(runtime)环境来处理JavaScript发出的网络请求。

请求发送方式及过程



对于常用的网络请求对象:XMLHttpRequest(XHR)、Fetch及WebSocket,熟悉前端开发的同学应该非常了解。XHR是Web开发中用得比较多的发送请求的方式,FetchWebsocket也是后起之秀,在很多现代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提供了一种更加简单,高效的方式来跨网络异步获取资源,同时可操纵RequestResponse对象来复用请求。

但是在React Native中,为了兼容两种平台的差异,采用了依赖于XMLHttpRequestFetch 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查看应用的网络请求,但是我们可以通过FiddlerCharlesProxyWireshark等软件设置代理,来查看追踪调试网络请求。这里使用Fiddler来作为代理。

  1. 首先设置Fiddler的代理端口:

    打开Filddler -> Tool -> Options -> Connects,在监听端口处填写相应的端口号,

  2. 在调试机器上、Android或者IOS模拟器模拟器中设置代理:

    找到调试的机器上的网络设置中,设置当前连接的WIFI的代理地址

  3. 刷新应用,在fiddler中查看网络请求(提示:右键,在新页签中打开可查看清晰图片):

在代理应用中,我们可以查看请求头,返回头,返回结果等相关的网络信息。当然,还可以根据相关代理软件拦截请求,重新设置后发送。

使用Reactotron调试网络

上面通过设置代理的方式来查看和追踪网络请求,虽然功能强大,但是实际操作起来有些难度,上手成本比较高。通过使用Reactotron,可以将调试的配置信息集成到应用中,方便在不同的开发环境下有相同的调试配置,节约开发配置成本。

Reactotron由两部分组成,一部分是调试应用,一部分是调试配置。

  1. 调试应用分别有各个操作系统的GUI安装版本。当然,如果习惯使用命令行,也可以使用NPM安装reactotron-cli
npm install -g reactotron-cli

  1. 设置调试配置:
在你的`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 https://github.com/facebook/react-native/issues/934

具体实现在XHRInterceptor.js中。原来的XMLHttpRequest 被改写成了 originalXMLHttpRequest,所以要在Chrome 中显示network 只需要替换 XMLHttpRequestoriginalXMLHttpRequest。在入口文件处设置:

if (__DEV__) {
GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest
}

当然,这样有可能会产生CORS, Chrome 会限制跨域请求。这时要么后端配合一下去除限制,要么使用 Allow-Control-Allow-Origin: * 插件。

React Native发送二进制数据(binary data )

由于React NativeFetch对象的底层采用的是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,会显著提高开发效率。

参考

Network layer in React Native

reactotron介绍

reactotron

Reactotron on React Native

推荐: 翻译项目Master的自述:

1. 干货|人人都是翻译项目的Master

2. iKcamp出品微信小程序教学共5章16小节汇总(含视频)

3. 开始免费连载啦~每周2更共11堂iKcamp课|基于Koa2搭建Node.js实战项目教学(含视频)| 课程大纲介绍

React Native 网络层分析的更多相关文章

  1. Hybrid APP基础篇(二)->Native、Hybrid、React Native、Web App方案的分析比较

    说明 Native.Hybrid.React.Web App方案的分析比较 目录 前言 参考来源 前置技术要求 楔子 几种APP开发模式 概述 Native App Web App Hybrid Ap ...

  2. React Native 从入门到原理

    React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原理的文章却寥寥无几. 本文分为两个部分:上半部分用通 ...

  3. 关于React Native 火热的话题,从入门到原理

    本文授权转载,作者:bestswifter(简书) React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原 ...

  4. React Native 从入门到原理一

    React Native 从入门到原理一 React Native 是最近非常火的一个话题,介绍如何利用 React Native 进行开发的文章和书籍多如牛毛,但面向入门水平并介绍它工作原理的文章却 ...

  5. React Native 入门到原理(详解)

    抛砖引玉(帮你更好的去理解怎么产生的 能做什么) 砖一.动态配置 由于 AppStore 审核周期的限制,如何动态的更改 app 成为了永恒的话题.无论采用何种方式,我们的流程总是可以归结为以下三部曲 ...

  6. React Native分析(index.ios.js)

    定义创建组件MyComponent(index.ios.js): 'use strict' var React = require('react-native'); var { AppRegistry ...

  7. React Native 4 for Android源码分析 一《JNI智能指针之介绍篇》

    文/ Tamic: http://blog.csdn.net/sk719887916/article/details/53455441 原文:http://blog.csdn.net/eewolf/a ...

  8. H5、React Native、Native应用对比分析

    每日更新关注:http://weibo.com/hanjunqiang  新浪微博!iOS开发者交流QQ群: 446310206 "存在即合理".凡是存在的,都是合乎规律的.任何新 ...

  9. React Native超简单完整示例-tabs、页面导航、热更新、用户行为分析

    初学React Native,如果没有人指引,会发现好多东西无从下手,但当有人指引后,会发现其实很简单.这也是本人写这篇博客的主要原因,希望能帮到初学者. 本文不会介绍如何搭建开发环境,如果你还没有搭 ...

随机推荐

  1. 从一篇ICLR'2017被拒论文谈起:行走在GAN的Latent Space

    同步自我的知乎专栏文章:https://zhuanlan.zhihu.com/p/32135185 从Slerp说起 ICLR'2017的投稿里,有一篇很有意思但被拒掉的投稿<Sampling ...

  2. 为eclipse离线安装hibernate tools插件

    工具/原料   eclipse luna jboss hibernate tools 方法/步骤     下载hibernate tools插件   在eclipse根目录下创建myplugins和l ...

  3. redis主从配置+哨兵模式

    1.搭建redis主从,一个master两个slave,加一个哨兵监听(sentinel),可以新建三个虚拟机,模拟环境,我的电脑没那么多虚拟机,就在一台虚拟机上弄的. 2.安装redis,如果是三台 ...

  4. 《编程语言实现模式》【PDF】下载

    <编程语言实现模式> 编程语言实现模式旨在传授构建语言应用(工具)的经验和理念,教读者构建自己的语言应用.这里的语言应用并非特指用编译器或解释器实现编程语言,而是泛指任何处理.分析.翻译输 ...

  5. 《深入理解计算机系统》【PDF】下载

    <深入理解计算机系统>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382303 内容提要 本书主要介绍了计算机系统的基本概念,包 ...

  6. IX-Protected Dataplane Operating System解读

    一.概述 商业操作系统在应用程序每秒钟需要数百万次操作时才能保持高吞吐量和低(尾)延迟,对于最慢的请求只需几百微秒.通常认为对于高性能网络(小信息的高包率.低延迟)的构建,最好都是在内核之外构建用户态 ...

  7. iOS 5个Xcode开发调试技巧

    转自Joywii的博客,原文:Four Tips for Debugging in XCode Like a Bro    1.Enable NSZombie Objects(开启僵尸对象) Enab ...

  8. c语言文件分割与合并

    一.综述 c语言操作文件通过文件指针FILE*,每个要操作的文件必须打开然后才能读写. 注意事项: @1分割与合并文件最好使用二进制模式即"rb"或"wb",这 ...

  9. 记录一笔关于PHPEXCEL导出大数据超时和内存溢出的问题

    通过查阅资料可以找到PHPEXCEL本身已经有通过缓存来处理大数据的导出了.但是昨晚一直没有成功,这可捉急了.最后想来想去就替换了phpExcel的版本了.最后就成功了.话不多说,代码附上 <? ...

  10. mkdir 命令详解

    rmdir  <man.linuxde.net> 作用: rmdir 命令用来创建目录,该命令创建由dirname 命名的目录.如果在目录名的前面没有添加任何路径名,则在当前目录下创建由d ...