Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

7、请求相关 #8

Open
CodingMeUp opened this issue Nov 15, 2017 · 1 comment
Open

7、请求相关 #8

CodingMeUp opened this issue Nov 15, 2017 · 1 comment

Comments

@CodingMeUp
Copy link
Owner

CodingMeUp commented Nov 15, 2017


fetch

之前在项目中用到了这个fetch 来代替Ajax 进行网络请求。也踩了不少的坑,在这边列举出来以及他的解决方法。

如何保持每次请求的会话一致

在用fetch进行网络请求的时候,发现每次请求到服务端的时候,他的sessionId 都是不一样的,后面排查原来是在请求的时候fetch默认是不会带上本地jsessionId,以至于服务端无法接收到,所以会重新创建一个新的session。

解决办法:
var init = {
  credentials: 'include' // 请求带上cookies,是每次请求保持会话一直
  ...		
}

兼容性,支持IE10+、谷歌、火狐等

由于fetch是一个新技术,有些旧的浏览器对它并不支持,这时候要怎么兼容?

解决办法:

引入一个额外的补丁es6-promise.js可以使它很好的支持IE9以上的版本,那IE8呢?IE8 需要改fetch.js源码才能支持
fetch.js 只需改两处:

...
try{  
  Object.getOwnPropertyNames(headers).forEach(function(name) {  
    this.append(name, headers[name])  
  }, this)  
}catch(e){  
  var a = [];  
    for (var i in headers) {  
    if (headers.hasOwnProperty(i)) {  
      a.push(i); // 输出 foo asj  
    }  
  };  
  a.forEach(function(name) {  
    this.append(name, headers[name])  
  }, this)  
 }  
}

Headers.prototype.forEach = function(callback, thisArg) {  
  try{  
    Object.getOwnPropertyNames(this.map).forEach(function(name) {  
      this.map[name].forEach(function(value) {  
        callback.call(thisArg, value, name, this)  
      }, this)  
    }, this)  
  }catch(e){  
    var a = [];  
    for (var i in this.map) {  
      if (this.map.hasOwnProperty(i)) {  
        a.push(i); // 输出 foo asj  
       }  
    };  
    a.forEach(function(name) {  
      this.map[name].forEach(function(value) {  
        callback.call(thisArg, value, name, this)  
      }, this)  
    }, this)  
  }  
} 
...

try里面的是源码本身的,catch里面的是小编额外加的。细心的会发现这两处改的是一样的,都是对getOwnPropertyNames进行替换。但是IE8也不支持forEach为啥没有替换 呢? 其实有替换了,只是不再这边体现。我把它写进了Array.prototype里面了,其实fetch.js里面也用到了indexOf,它在IE8下也是不支持,我把它一并的放在Array.prototype;如下:

Array.prototype.forEach = function(callback, thisArg) {  
	var T, k;  
	if (this == null) {  
		throw new TypeError(" this is null or not defined");  
	}  
	var O = Object(this);  
	var len = O.length >>> 0; // Hack to convert O.length to a UInt32  
	if ({}.toString.call(callback) != "[object Function]") {  
		throw new TypeError(callback + " is not a function");  
	}  
	if (thisArg) {  
		T = thisArg;  
	}  
	k = 0;  
	while (k < len) {  
		var kValue;  
		if (k in O) {  
			kValue = O[k];  
			callback.call(T, kValue, k, O);  
		}  
		k++;  
	}  
};

Array.prototype.indexOf = function(elt /*, from*/){
	var len = this.length >>> 0;
	var from = Number(arguments[1]) || 0;
	from = (from < 0)
			? Math.ceil(from)
			: Math.floor(from);
	if (from < 0)
		from += len;
	for (; from < len; from++)
	{
		if (from in this &&
			this[from] === elt)
		return from;
	}
	return -1;
};
@CodingMeUp CodingMeUp changed the title 7、js中this的问题 call bind apply 7、TODO Dec 12, 2017
@CodingMeUp
Copy link
Owner Author

CodingMeUp commented Dec 20, 2017


Axios使用及源码分析

背景

工程院推进的React脚手架使用Axios网络库,Vue2.0也推荐使用。结合平常开发过程中的使用,总结遇到的问题以及分析部分核心源码。

Axios介绍

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中

它有以下特性:
  • 支持Promise API
  • 支持request和response拦截
  • 转换请求与响应数据
  • 取消请求
  • 支持客户端XSRF攻击防护
  • 支持各主流浏览器及IE8

Axios基本使用

一般使用请求如下,符合Restful风格

axios.request(config)
axios.get(url, config)
axios.delete(url, config)
axios.head(url, config)
axios.post(url, data, config)
axios.put(url, data, config)
axios.patch(url, data, config)

config配置参数可自定义:
config = {
   baseURL: 'http://esp-homework-api',   //请求域名
   headers: XXX,   //请求头,可添加自定义参数
   ignoreErrorCodes: ['XXX','XXX'],  // 全局统一拦截时,如果配置该参数,则不会走错误拦截处理
   methods: 'POST',    //没设置methods时,默认是GET请求
   timeout: 500
}
  • Axios并发请求
function getUser1() {
  return axios.get('/user/818118');
}
function getUser2() {
  return axios.get('/user/910604');
}
axios.all([getUser1(), getUser2()])
  .then(axios.spread(function (data1, data2) {
    // 两个请求都完成时,处理逻辑,data1、data2分别代表两个请求返回的结果
  }));
  
Axios实际上是一个Promise,所以上述请求等同于下面的写法:
  Promise.all([getUser1(), getUser2()]).then(([data1, data2])=> {
      两个请求都完成时,处理逻辑
  })
  • Axios请求返回的内容
{
  data:{},      //真实返回数据,前端需要处理的
  status:200,
  statusText:'OK',
  headers: {},
  config: {}
}

可以测试下请求返回数据
axios.get('/user/818118')
  .then(function(res){
    console.log(res.data);
    console.log(res.status);
    console.log(res.statusText);
    console.log(res.headers);
    console.log(res.config);
  })
  • 拦截器
网络请求发出去之前进行拦截:

    axios.interceptors.request.use(function(config){
      return config;
    },function(err){
      return Promise.reject(error);
    });

    实际使用场景,可以设置auth参数:
    axios.interceptors.request.use(function(config=> {
      config.headers['Authorization'] = 'xxx'  
    })

网络请求返回之前进行拦截:

    axios.interceptors.response.use(function(res){
      return res;
    },function(err){
      return Promise.reject(error);
    });

    实际使用场景,因为axios实际返回是一个结构体,包含data数据,所以我们可以直接拦截返回给前台调用:
    axios.interceptors.response.use(function(response=> {
      return response.data
    })

源码分析

主要分析拦截器的相关源码,在这之前,我们有必要回顾下Promise的知识

    getJSON("/post/1.json").then(function(post) {
      return getJSON(post.commentURL);
    }).then(function funcA(comments) {
      console.log("resolved: ", comments);
    }, function funcB(err){
      console.log("rejected: ", err);
    });

第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就调用funcA,如果状态变为rejected,就调用funcB

整个拦截的过程,大概如下:

interceptors.request -> request -> interceptors.response -> response

我们现在来看下Axios类的定义

function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

结合上面的使用方式axios.interceptors.request.use,interceptors主要是由一个InterceptorManager类的实例实现。

InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};

InterceptorManager的功能简单地讲,只是通过一个数组来维护拦截器,每个拦截器的两个参数分别作为Promise中的resolve和reject

InterceptorManager的使用是在Axios类中

Axios.prototype.request = function request(config) {

  // 挂载interceptor中间件
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);

  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
};

当执行request请求时,会遍历之前定义的interceptors拦截器,通过一个数组chain维护,经过遍历逻辑后,chain变量变为:

[interceptor.request.fulfilled, interceptor.request.rejected, 
dispatchRequest, undefined, 
interceptor.response.fulfilled, interceptor.response.rejected]

while方法循环包装成一个promise返回:
Promise.resolve(config)
    .then(interceptor.request.fulfilled, interceptor.request.rejected)
    .then(dispatchRequest, undefined)
    .then(interceptor.response.fulfilled, interceptor.response.rejected)
    
这样就是之前我们说的promise嵌套使用。
  • 遇到问题

开发过程中,有使用过实例化axios的方法

    var instance = axios.create({
      baseURL:"https://some-domain.com/api/"});

但是新实例化的对象不会执行之前定义的拦截器方法,我们分下下创建实例的源码

function createInstance(defaultConfig) {
  var context = new Axios(defaultConfig);
  var instance = bind(Axios.prototype.request, context);

  utils.extend(instance, Axios.prototype, context);
  utils.extend(instance, context);

  return instance;
}

// Factory for creating new instances
axios.create = function create(instanceConfig) {
  return createInstance(utils.merge(defaults, instanceConfig));
};

因为重新实例化后,不会将原先定义的参数同步过来,需要自己重新配置

总结

Axios的有点在于既能在web,又能在node中使用,而且轻量级。我们在阅读源码时,可以学习它的封装思路,有助于在工程中实践。

@CodingMeUp CodingMeUp changed the title 7、TODO 7、请求相关 Dec 20, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant