React Native 网络层分析

Stella981
• 阅读 452

文:志俊(沪江Web前端)

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

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

React Native 网络层分析

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

请求发送方式及过程

React Native 网络层分析 对于常用的网络请求对象: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,在监听端口处填写相应的端口号, React Native 网络层分析

  2. 在调试机器上、Android或者IOS模拟器模拟器中设置代理: 找到调试的机器上的网络设置中,设置当前连接的WIFI的代理地址 React Native 网络层分析

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

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

使用Reactotron调试网络

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

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

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

    npm install -g reactotron-cli

React Native 网络层分析

  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请求路径
     }))
    

    React Native 网络层分析 React Native 网络层分析 React Native 网络层分析

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编码的字符串是个不错的选择。 React Native 网络层分析

例如,你从服务器下载一张图片(注意:不是通过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

React Native 网络层分析

React Native 网络层分析

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

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

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

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

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Python之time模块的时间戳、时间字符串格式化与转换
Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y
Easter79 Easter79
2年前
SpringMvc接受特殊符号参数被转义
WEB开发时,在前端通过get/post方法传递参数的时候 如果实参附带特殊符号,后端接收到的值中特殊符号就会被转义例如该请求: http://localhost:10001/demo/index.do?name张三(1)注:中文()不会出现此种情况后台就收到的实际name值为:  张三&40;1&41;&40;其实为h
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这