71

React Native 网络层分析

 6 years ago
source link: http://mp.weixin.qq.com/s/ksX4LZNdjjyWSAfUCRcR1w
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

Image

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

请求发送方式及过程

Image

对于常用的网络请求对象:XMLHttpRequest(XHR)、Fetch 及 WebSocket,熟悉前端开发的同学应该非常了解。 XHR是Web开发中用得比较多的发送请求的方式, Fetch和 Websocket也是后起之秀,在很多现代 Web 应用中得以采用。但是,在 ReactNative中,这些对象的使用和 Web 应用是有差别的。当你在 JS 层调用网络请求时,其实是经历了两个过程才到达真正的服务器端。就像头部 banner 表示的那样。

XMLHttpRequest(XHR)

在 ReactNative中, XMLHttpRequest(XHR)由两部分组成: “前端”(front-end)和“后端”(back-end)。前端负责与 JavaScript交互,后端负责在原生平台上转换 JavaScript发送过来的请求为原生系统自己的请求。

这里的后端其实是一个原生平台顶层抽象的统一API层,使得 JavaScript层可以调用原先系统的网络模块。例如 iOS下内置的 URLSession 模块和 Android下的 OKHTTP模块。

Fetch

在现代 Web 浏览器中, FetchAPI 提供了和 XHR大部分相同的功能,但是 Fetch提供了一种更加简单,高效的方式来跨网络异步获取资源,同时可操纵 Request和 Response对象来复用请求。

但是在 ReactNative中,为了兼容两种平台的差异,采用了依赖于 XMLHttpRequest的 Fetch Polyfill 来实现这个请求对象。这就意味着我们不能像实用 Web 平台下的 Fetch对象一样来实用 ReactNative下的该对象。比如采用这个对象来发送 binary 数据。当然可以采用第三方的库比如 react-native-fetch-blob 来实现相应的功能。

Websocket

Websocket作为一种新的通信协议,采用全双工通讯方式与服务器间进行通信的网络技术。

在 ReactNative中, Websocket并不是一个独立的请求,和 XMLHttpRequest(XHR)一样由两部分组成: “前端”(front-end)和“后端”(back-end)。前端负责与 JavaScript交互,后端负责在原生平台上转换 JavaScript发送过来的请求为原生系统自己的请求。在 iOS 中采用的是自己开发的 NSStream,而在 Android 系统中则是OKHTTP 模块。

查看React Native中的网络请求

在 ReactNative开发中,你可以通过 ChromeDeveloperTools(CDT)的 Sources面板中调试 javascript部分的代码,包括断点、输出信息、断点调试等一切 javascript调试所需的信息。但是,唯一缺少的就是网络请求的跟踪调试。我们没办法像 Web 开发那样,可以通过 CDT中的网络面板( Network)来查看应用的网络请求的相关信息。

使用代理调试网络请求

虽然没有办法通过 CDT查看应用的网络请求,但是我们可以通过 Fiddler、 CharlesProxy及 Wireshark等软件设置代理,来查看追踪调试网络请求。这里使用 Fiddler来作为代理。

1.首先设置 Fiddler的代理端口: 打开 Filddler -> Tool -> Options -> Connects,在监听端口处填写相应的端口号,

Image

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

Image

3.刷新应用,在 fiddler中查看网络请求

Image

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

使用 Reactotron 调试网络

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

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

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

Image

2.设置调试配置:



  1. 在你的`React Native`应用中安装`reactotron-react-native`

  2.  ```

  3.  npm i --save-dev reactotron-react-native

  4.  ```

  5.  然后,在你的应用的添加配置文件,定制调试内容:

  6.  ```

  7.  import Reactotron, {

  8.    trackGlobalErrors,

  9.    openInEditor,

  10.    overlay,

  11.    asyncStorage,

  12.    networking

  13.  } from 'reactotron-react-native'

  14.  Reactotron

  15.    .configure({

  16.      name: 'xxx'                 // 调试的名称

  17.    })

  18.    .use(trackGlobalErrors())     // 设置监听全局错误

  19.    .use(openInEditor())          // 设置在编辑器中打开错误

  20.    .use(overlay())               // 设置图片遮盖图片(用于UI还原度对比)

  21.    .use(asyncStorage())          // 设置异步存储调试

  22.    .use(networking())            // 设置网络调试

  23.    .connect()                    // 连接应用(必须)

  24.  ```

  25.  然后在你的应用的入口文件中引入这个配置文件。然后重新启动应用。当然,还可以使用正则表达式过滤请求的`contentType`的类型和要忽略的请求的`url`,见下面的配置:

  26.  ```

  27.   .use(networking({

  28.      ignoreContentTypes: /^(image)\/.*$/i,   // 设置reactotron要忽略的文件类型

  29.      ignoreUrls: /\/(logs|symbolicate)$/,    // 设置reactotron要忽略的url请求路径

  30.   }))

  31.  ```

Image
Image
Image

reactotron调试网络只是他的一个功能之一,其他还有很多强大的功能。有兴趣可以查看他的文档。

使用 Chrome Developer Tools 网络面板调试网络

ReactNative默认暴露出来的接口中,是没有直接在 ChromeDeveloperTools查看网络请求的方法的,查看 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。在入口文件处设置:



  1. if (__DEV__) {

  2.  GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest

  3. }

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

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

由于 ReactNative中 Fetch对象的底层采用的是 XHR实现,这就限制了发送二进制数据的功能。当然 ReactNative提供了一系列的方式来解决这个问题,比如: 转换二进制文件为 Base64 字符串或者采用第三方库 react-native-fetch-blob。但是并没有从底层解决这个问题。

转换二进制为 Base64 发送

到目前为止, ReactNative不能发送非序列化的数据,所以,要发送二进制数据,采用 Base64 编码的字符串是个不错的选择。 

Image

例如,你从服务器下载一张图片(注意:不是通过 url从服务器获取),请求通过JavaScript 线程,再通过 ReactNative提供的桥接器,最后通过原生系统的网络模块发送到服务端。服务端返回一个 Base64 编码过的图片, JavaScript线程收到返回的字符串后,会分配相应的内存,然后 ReactNative会调用相应的原生模块渲染成相应图片。但是值得主要的是,这种方式会造成典型的性能问题——内存泄漏。

通过 Base64 编码的方式传输二进制文件,这里会造成一系列性能问题,这篇文章中列出了大部分性能问题及提出了相应的解决方案。

现在使用的各种方法发送二进制文件都存在各种问题,最终的解决方式是要相应的标准能够实现二进制的传输。目前, WebSocket已经支持了二进制传输。在最新版本的 ReactNative层也已经支持 WebSocket协议来传输二进制文件,但是,相应的原生平台的网络模块暂时还不支持。

ReactNative开发方式是非常不错的体验,但是,受各个平台差异和标准的限制,不得不折中处理一些问题,随之而来的是相应的性能、效率的问题。另外,采用开发,性能上和用户体验上和原生应用还是有一定差距。但是如果在原生应用中能够集成 ReactNative,会显著提高开发效率。

Network layer in React Native

  •     https://medium.com/dailyjs/network-layer-in-react-native-eec841f11861

reactotron介绍

  •     https://infinite.red/reactotron

reactotron

  • https://github.com/infinitered/reactotron

Reactotron on React Native

  • https://github.com/infinitered/reactotron/blob/master/docs/quick-start-react-native.md

Image

Image

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK