封装ajax函数支持jsonp,手写ajax jsonp 以及对fetch的封装

夫君子之行,静以修身,俭以养德。 ——诸葛亮

foreword

前端需要反复温故,以及敢于尝试新的技术。jQuery,Vue,React 用了好几年,我们习惯性使用ajax库进行一层简易的封装,然后向后台请求数据,而事实接触原生js请求并不是很多,所以手写了ajax jsonp fetch的封装,用来温故一下以前的知识.

一. 使用XMLHttpRequest 实现ajax请求

注明:暂时不考虑IE5和IE6 ,所以没有做小的兼容性处理

code

const isFunction = fn => typeof fn === 'function';

const ajaxHttp = (url, option = {}) => {

if (!XMLHttpRequest) {

throw Error('您的浏览器版本过低,请选择新版本的浏览器');

}

let {

data = {},

method = 'GET',

// 是否异步 @async

async = true,

timeout = 0,

header = {},

ontimeout = () => {},

onerror = () => {},

onprogressFn

} = option;

method = method.toUpperCase();

const isGet = method === 'GET';

if (isGet) {

// format str 拼接

const str = Object.entries(data)

.join('&')

.split(',')

.join('=');

url = url.includes('?') ? `${url}&${str}` : `${url}${str ? '?' + str : ''}`;

}

return new Promise((resolve, reject) => {

const xhr = new XMLHttpRequest();

xhr.open(method, url, async);

xhr.timeout = timeout;

// 设置请求头 open方法之后 send方法之前调用

xhr.setRequestHeader('Content-Type', 'application/json');

Object.entries(header).forEach(item => {

xhr.setRequestHeader(item[0], item[1]);

});

// 错误回调

isFunction(onerror) ? (xhr.onerror = onerror) : '';

// 超时回调

isFunction(ontimeout) ? (xhr.ontimeout = ontimeout) : '';

// 判断onprogress是否支持 如果支持写入回调 这里一般用来获取上传进度的

if ('onprogress' in xhr.upload && isFunction(onprogressFn)) {

xhr.upload.onprogress = onprogressFn;

}

xhr.onreadystatechange = function() {

if (xhr.readyState === 4) {

if (xhr.status === 200) {

try {

resolve(JSON.parse(this.responseText));

} catch (error) {

resolve(this.responseText);

}

} else {

const resJson = {

code: this.status,

response: this.response

};

reject(resJson);

}

}

};

xhr.send(method === 'GET' ? null : JSON.stringify(data));

});

};

const creaetAjax = type => {

ajaxHttp[type] = (url, options) =>

ajaxHttp(url, {

...options,

method: type

});

};

const methods = ['get', 'GET', 'POST', 'post', 'put', 'PUT', 'DELETE', 'delete'];

// 写入各个方法,可以使用ajaxHttp.get 等调用

methods.forEach(type => creaetAjax(type));

sync 注释

默认值为true,即为异步请求,若async=false,则为同步请求

当xhr为同步请求时,有如下限制:

xhr必须为 0

xhr.withCredentials必须为false

xhr.responseType 必须为"" (注意置为"text"也不允许)

如果上面任何一个条件不满足,都会报错,所以我们一般要避免sync请求.

二.对原生fetch进行简易的封装

code

const errStatusReg = /[4,5]\d{2}/;

const fetchHttp = (url,options={})=>{

let {

method="GET",

headers={},

data={},

mode="cors",

credentials = "omit",

redirect = "manual",

cache = "default"

} = options;

method = method.toUpperCase();

const isGet = method === 'GET';

if(isGet){

// format str 拼接

const str = Object.entries(data).join('&').split(',').join('=');

url = url.includes("?")?`${url}&${str}`:`${url}${str?'?'+ str:''}`;

};

return fetch(url,{

method,

// 请求的 body 信息:可能是一个 Blob、BufferSource、FormData、URLSearchParams

// 或者 USVString 对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。

body:isGet?null:JSON.stringify(data),

// http 请求头

headers,

// 请求模式 @注释1

mode,

// 表达的含义请求是否携带cookie @注释2

credentials,

// 重定向 模式 @注释3

redirect,

// 请求的缓存模式 @注释4

cache

}).then(data=>{

if (errStatusReg.test(data.status))throw Error(`${data.status}:${data.statusText}`);

return data.json();

})

};

const creaetFetch = (type)=>{

fetchHttp[type] = (url,options)=>fetchHttp(url,{...options,method:type})

};

const methods = ["get","GET",'POST',"post","put","PUT","DELETE","delete"];

methods.forEach(type => creaetFetch(type));

注释解析

@1 请求模式 mode

常用的 mode 属性值:

1. same-origin: 表示只请求同域.如果你在该 mode 下进行的是跨域的请求的话, 那么就会报错.

2. no-cors: 正常的网络请求, 主要应对于没有后台没有设置 Access-Control-Allow-Origin.话句话说, 就是用来处理 script, image 等的请求的.他是 mode 的默认值.

3. cors: 用来发送跨域的请求.在发送请求时, 需要带上.

4. cors-with-forced-preflight: 这是专门针对 xhr2 支持出来的 preflight(多发一个options请求), 会事先多发一次请求给 server, 检查该次请求的合法性。

@2 credentials(凭证)

omit: 默认值, 忽略cookie的发送

same-origin: 表示cookie只能同域发送, 不能跨域发送

include: cookie既可以同域发送, 也可以跨域发送

@3 redirect

可用的 redirect 模式: follow(自动重定向), error(如果产生重定向将自动终止并且抛出一个错误), 或者 manual(手动处理重定向).在Chrome中, Chrome 47 之前的默认值是 follow, 从 Chrome 47 开始是 manual。

@4 cache

default :表示fetch请求之前将检查下http的缓存

no-store: 表示fetch请求将完全忽略http缓存的存在, 这意味着请求之前将不再检查下http的缓存, 拿到响应后, 它也不会更新http缓存

no-cache: 如果存在缓存, 那么fetch将发送一个条件查询request和一个正常的request, 拿到响应后, 它会更新http缓存

reload:表示fetch请求之前将忽略http缓存的存在, 但是请求拿到响应后, 它将主动更新http缓存)

force-cache: 表示fetch请求不顾一切的依赖缓存, 即使缓存过期了, 它依然从缓存中读取.除非没有任何缓存, 那么它将发送一个正常的request

only-if-cached: 表示fetch请求不顾一切的依赖缓存, 即使缓存过期了, 它依然从缓存中读取.如果没有缓存, 它将抛出网络错误(该设置只在mode为”same-origin” 时有效).

注明:fetch这块原生就存在了很友好的API,这块可以参考MDN官方文档 fetch

三.jsonp封装

code

const formatData = data => {

// 这里要先处理特殊情况的字符 & = 等等

return Object.entries(data)

.map(item => [encodeURIComponent(item[0]), item[1]])

.join('&')

.split(',')

.join('=');

};

/**

*

*

* @param {*} url 传入的请求url

* @param {*} options 包含两个参数 1. get请求的参数对象data,以及和后台协商好的回调函数名称

* @returns

*/

const jsonpHttp = (url, options) => {

options = options || {};

options.data = options.data || {};

const headDOM = document.querySelector('head');

const scriptDOM = document.createElement('script');

// 这里传入的是url所以存在一些特殊的操作

const str = formatData(options.data);

return new Promise((resolve, reject) => {

const CB = jsonoResData => {

window[options[`jsonpCB`]] = null;

headDOM.removeChild(scriptDOM);

clearTimeout(scriptDOM.timer);

resolve(jsonoResData);

};

window[options[`jsonpCB`]] = CB;

// 超时处理

if (options.timeout) {

scriptDOM.timer = setTimeout(() => {

headDOM.removeChild(scriptDOM);

window[options[`jsonpCB`]] = null;

reject({

timeout: true,

message: '请求超时'

});

}, options.timeout * 1000);

}

const urlFormat = url.includes('?') ? `${url}&${str}` : `${url}${str ? '?' + str : ''}`;

// 多加一个jsonp的回调函数名称

scriptDOM.src = `${urlFormat}&jsonpCB=${options[`jsonpCB`]}`;

headDOM.appendChild(scriptDOM);

});

};

如有纰漏之处望能够给出指正,有帮助的话动动小手点个赞把。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值