React Native打造自己的fetch(二)

来源:互联网 发布:mac设置svn用户名密码 编辑:程序博客网 时间:2024/06/08 12:06

前言:前一篇 React Native打造自己的fetch(一)我们已经通过源码带大家走了一遍fetch,感觉吧也不是那么有难度,总结起来就是:

  1. rn端通过fetch传递url跟一些参数(header、body)
  2. 对传入的url跟参数做一系列的封装,封装成
 var request = new Request(input, init)

然后通过request对象把请求需要的设置给XMLHttpRequest对象。

3.通过XMLHttpRequest对象调取native的网络请求方法
4. native网络请求完毕后发通知给rn端

 public static void onDataReceived(RCTDeviceEventEmitter eventEmitter, int requestId, String data) {        WritableArray args = Arguments.createArray();        args.pushInt(requestId);        args.pushString(data);        eventEmitter.emit("didReceiveNetworkData", args);    }

5、rn端接收到通知回调onload方法,返回结果。

如果还有不懂的小伙伴可以看我上一片文章。

我们去copy一份fetch的源码然后放入我们自己的工程中,我们取名叫xxxFetch.js,小伙伴自己取名字哈。
xxx/node_modules/whatwg-fetch

(function (self) {    'use strict';    //这里换成我们的ocjFetch    if (self.ocjFetch) {        return    }    ......      self.Headers = Headers    self.Request = Request    self.Response = Response    self.ocjFetch = function (input, init, xhrSelf) {        return new Promise(function (resolve, reject) {            var request = new Request(input, init)            var xhr = xhrSelf || new XMLHttpRequest()            xhr.onload = function () {                var options = {                    status: xhr.status,                    statusText: xhr.statusText,                    headers: parseHeaders(xhr.getAllResponseHeaders() || '')                }                options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')                var body = 'response' in xhr ? xhr.response : xhr.responseText                resolve(new Response(body, options))            }            xhr.onerror = function () {                reject(new TypeError('Network request failed'))            }            xhr.ontimeout = function () {                reject(new TypeError('Network request failed'))            }            xhr.open(request.method, request.url, true)            if (request.credentials === 'include') {                xhr.withCredentials = true            }            if ('responseType' in xhr && support.blob) {                xhr.responseType = 'blob'            }            request.headers.forEach(function (value, name) {                xhr.setRequestHeader(name, value)            })            //我们只需要加上下面这段代码即可            if (init && init !== null && typeof init.timeout !== 'undefined') {                xhr.timeout = init.timeout;            }            xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)        })    }     //这里换成我们的ocjFetch    self.ocjFetch.polyfill = true})(typeof self !== 'undefined' ? self : this);

我们该怎么用起来呢?

首先在你的项目开始中倒入ocjfetch.js文件(我们直接在我们的index.ios.js文件中导入了,当然android里面也是要导入的):

import 'xxx/ocjFetch';

然后我们就可以开心的用起来了:

  ocjFetch('http://blog.csdn.net/vv_bug/article/details/77368621').then((response) => response.text()).then((responseText) => {            console.log('responseText-->'+responseText);        }).catch((error) => {            console.error(error);        });

然后我们看一下我们请求的结果:

responseText--><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">    <html xmlns="http://www.w3.org/1999/xhtml"><head>              <link rel="canonical" href="http://blog.csdn.net/vv_bug/article/details/77368621"/>  <meta http-equiv="Cache-Control" content="no-siteapp" /><link rel="alternate" media="handheld" href="#" />    <meta name="shenma-site-verification" content="5a59773ab8077d4a62bf469ab966a63b_1497598848">     <title>React Native打造自己的fetch(一) - vv_bug        - CSDN博客</title>    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    <meta name="description" content="前言:项目终于要结束了,终于是过上了正常上班族的日子了,说多了都是泪啊,感觉短短几个月下来都有点怀疑人生了,怪不得说程序猿是一个吃青春饭的职业,哈哈~就像这只笔一样~~~ 项目开始之前:项目结束这只笔是这样的:哈哈~~进入我们今天的主题哈,项目快结束了,所以每天都在考虑怎么优化之前写的代码,于是看了看之前的网络请求,然后顺便看了看fetch的源码,觉得还是应该把自己理解记录下来~ 大牛略过哈!先看" />    <meta name="keywords" content="" />    .....

太长了,我就截取一点点哈~ 有小伙伴说了,有fetch了我们干嘛要封装一个叫ocjfetch的东西呢? 首先fetch已经作为一个网络请求库已经对request跟response封装的很好了,但是呢?比如我们需要去设置网络请求失败后提示语而不是默认的:

 xhr.onerror = function () {                reject(new TypeError('Network request failed'))            }            xhr.ontimeout = function () {                reject(new TypeError('Network request failed'))            }

或者我们需要监听上传获取的进度条、亦或者我们需要结束我们的网络请求等等…又有小伙伴想说了,我们可以直接用XMLHttpRequest发请求啊,都不用fetch了,我想说,这正是我们需要往下研究的东西哈~~ 但是基于普通的项目,封装或改变一下fetch已经足够我们使用了,研究再深一点就是去研究XMLHttpRequest,然后直接跟native交互了,好啦~ 废话不多说了。我们简单封装一下,使得我们最后请求 变成这样的:

  new LoginRequest('POST',{name:'yasin',pwd:'123456'}).start((response)=>{        },(erro)=>{        });

直接贴上封装的BaseRequest.js文件了:

import  {    Alert,    Platform,    ToastAndroid,}from 'react-native'import AppConst, {DEBUG_MODE} from '../../app/constants/AppConstant';export default class BaseRequest {    // 构造    constructor(body, method, mode) {        if (body == null) {            body = {};        }        //默认的一些参数比如Global.testToken,发布版请改为Global.token        Object.assign(body, {            version: AppConst.version,            access_token: 'xxx' //测试token        });        //当没有指定请求方法的时候默认post        if (method == null) {            method = 'POST';        }        if (mode == null) {            mode = 'cors';        }        this.method = method;        this.body = body;        this.mode = mode;        this.headers = {            'Accept': 'application/json',            'Content-Type': 'application/json',        };    }    /**     * 请覆盖此方法     */    requestUrl() {        throw ({message: 'function requestUrl must be overrided!'});    }    /**     * 开启loadingdialog     * @returns {BaseRequest}     */    showLoadingView() {        //可以通过redux发送通知,然后显示进度条view        return this;    }    /**     * 关闭dialog     * @returns {BaseRequest}     */    dismissLoadingView() {        //可以通过redux发送通知,然后显示进度条view        return this;    }    /**     * 开始请求     * @param successCallBack 成功后的回调     * @param failCallBack 失败后的回调     * @returns {BaseRequest}     */    async start(successCallBack, failCallBack,onProgress) {        try {            this.xmlHttpRequest=new XMLHttpRequest();            if(onProgress)this.xmlHttpRequest.onProgress=onProgress;            let url = this.getBaseUrl() + this.requestUrl();            if ('GET' === this.method) {                let str = this.toQueryString(this.body);                if (str && str.length > 0) url += '?' + str;            }            let response = await ocjFetch(url, {                headers: this.headers,                method: this.method,                mode: this.mode,                body: this.method == 'GET' ? null : JSON.stringify(this.body),                timeout: 10 * 1000,//请求超时时间            },this.xmlHttpRequest);            let responseJson = await response.json();            if (this.isShowing) {                this.dismissLoadingView();            }            this.handleResponse(responseJson, successCallBack, failCallBack);        } catch (erro) {            if (this.isShowing) {                this.dismissLoadingView();            }            if (failCallBack && !this.isCancled) failCallBack(erro);        }    }    /**     * 处理response     * @param responseJson     * @param successCallBack     */    handleResponse(responseJson, successCallBack, failCallBack) {        if (this.isShowing) {            this.dismissLoadingView();        }        if (ResponseStatus.SUCCESS == responseJson.code || parseInt(responseJson.code) === ResponseStatus.SUCCESS_INT) {            if (successCallBack) successCallBack(responseJson);        } else if (responseJson.message && responseJson.message.length > 0 && this.isShowMessage) {            this.alertWithStr(responseJson.message);            if (failCallBack) failCallBack(responseJson);        } else {            if (failCallBack) failCallBack(responseJson);        }    }    /**     * 是否取消请求     * @param cancle     */    setCancled() {        if(this.this.xmlHttpRequest)this.xmlHttpRequest.abort();    }    /**     * 用于对对象编码以便进行传输     * @param obj 对象参数     * @returns {string} 返回字符串     */    toQueryString(obj) {        let str = '';        if (obj) {            let keys = [];            for (let key in obj) {                keys.push(key);            }            keys.forEach((key, index) => {                str += key + '=' + obj[key];                if (index !== keys.length - 1) {                    str += '&';                }            });        }        return str;    }    /**     * 打印后台message     * @param str     */    alertWithStr(str) {        TimerMiXin.setTimeout(() => {            if (Platform.OS == 'ios') {                Alert.alert(                    str,                    null,                    [                        {text: '确定'}                    ]                );            }            if (Platform.OS == 'android') {                ToastAndroid.show(str, ToastAndroid.LONG);            }        }, 1000);    }    /**     * 返回baseurl     */    getBaseUrl() {        return AppConst.BASE_URL;    }}

用法:

class LoginRequest extends BaseRequest{            requestUrl(){                return 'api/login.action';            }        }        new LoginRequest('POST',{name:'yasin',pwd:'123456'}).start((response)=>{            console.log('请求成功-->'+response);        },(erro)=>{            console.log('请求失败-->'+erro.message);        },({requestId,responseText,progress,total})=>{            console.log('请求进度-->'+progress/total);        });

哈哈~~ 有小伙伴可能又要问了,发个请求还要去继承一个BaseRequest,然后重写一个方法requestUrl,好麻烦啊,可能你简单的请求感受不到继承跟封装带来的优势,当你请求的数据很复杂的时候或者一个项目中请求很多的时候需要做统一处理的时候,这样封装就很爽了,举一个我们项目中一个简单的例子:

我们后台一个数据比较复杂的接口,返回的数据单单解析就几百行代码,如果我们把解析跟展示都放在一个文件中,是不是感觉很不爽呢?我们想做的是数据与页面分离开,解析过程页面不需要管,页面只需要它需要的数据,这个时候就可以去重写一下baserequest的handleResponse方法

class LoginRequest extends BaseRequest{            requestUrl(){                return 'api/login.action';            }            handleResponse(responseJson, successCallBack, failCallBack) {        super.handleResponse(responseJson, (responseJson)=> {        //定义好页面需要的数据            let response = {            };            //一系列的数据处理            .....            //处理完毕后返回页面response            successCallBack(response);        }, failCallBack);        }

我只是简单的举一个例子哈,感觉这一节有点脱离主题了哈~ 至于ocjfetch的一些,比如图片上传,https证书请求等功能,rn跟native早就为我们准备好了,留给小伙伴自己去发掘哈~~ 欢迎入群,欢迎交流~~~~