Ajax 封装解析
发布时间:2023-04-06 08:46:10 所属栏目:教程 来源:
导读:首先, 我们的 xhr 函数支持 config 传入, 内部通过 XMLHttpRequest 技术来进行请求的收发, 大致就是上面这样结构的代码,内部的实现我们前面章节都讲过,唯一不同的是,在 onreadystatechange 上,我们挂载的方法
|
首先, 我们的 xhr 函数支持 config 传入, 内部通过 XMLHttpRequest 技术来进行请求的收发, 大致就是上面这样结构的代码,内部的实现我们前面章节都讲过,唯一不同的是,在 onreadystatechange 上,我们挂载的方法最后使用 resolve() 来进行断言,这样做的目的是,后续可以通过 .then() 的方式进行数据操作。 method 标准化 首先, 用户传进来的 method 可能是大写也可能是小写,我们可以先做一个标准化,对 method 做一个转化,将其变为大写: method.toupperCase() 构建 url 有些同学很奇怪,为什么说构建 url,我们不是通过 config 传入 url 吗? 是的,但是同学你别忘了,我们支持 params! 因此,我们需要把 params 上的参数进行一定格式序列化拼接到 url 后面 ,构成 "url?a=xxx&b=xxx" 的格式。为此,我们需要提供了一个 buildUrl 的函数: /** * 构建 url * @param {*} url * @param {*} params */ function buildUrl(url, params) { if (!params || !isPlainObject(params)) return url; // 如果 params 没有传或者不是一个纯对象,直接返回原 url let values = []; Object.keys(params).forEach(key => { // 对 params 中的每一项进行处理 const val = params[key]; if (typeof val === undefined || val === null) { // 如果当前项的值为 undefined 或者 null,则忽略 return; } values.push(`${key}=${val}`); // 将 “key=value”的形式加入到 values 数组中 }); let serializedParams = values.join("&"); // 序列化,将 values 数组转化为字符串,格式为 "key=value&key=value" if (serializedParams) { // 如果有值,则加入到url后面。构成 "url?key=value&key=value" 的形式 url += (url.indexOf("?") === - ? "?" : "&") + serializedParams; } return url; } 在这个函数中,我们可以传参 url 和 params。如果传入params 为假值,那我们直接忽略,返回 url 即可。否则,我们需要对 params 中 的每一项目进行序列化,变为 "key=vaue" 这样的形式, 添加到 values 数组中。接着我们通过数组的 .join("&") 的方法,把 values 数组通过 “&” 进行拼接。最后拼接到 url 后面,构成 "url?key=value&key=value" 的形式返回。 这里,我们也涉及到了一个工具函数 isPlainObject,在本章节中好几处都会用到,他的作用是判断该对象是不是一个纯 “{}” 的对象,它的实现如下: const toString = Object.prototype.toString; // 由于 Object.prototype.toString 在判断类型的时候非常好用,并且用到的次数经常会比较多,我们通常可以这样缓存起来 /** * 判断当前 val 是否是一个纯对象 * @param {*} val */ function isPlainObject(val) { return toString.call(val) === "[object Object]"; } 标准化 data 因为 .send() 是无法支持 Json 格式数据的,所以我们需要对 data 做一个序列化处理: /** * 处理 data,因为 send 无法直接接受 json 格式数据,这里我们可以直接序列化之后再传给服务端 * @param {*} data */ function transformData (data) { if (isPlainObject(data)) { return JSON.stringify(data) } return data } 实现非常简单,如果判断 data 是一个纯对象的话,就加一道 JSON.stringify(data) 的操作进行序列化, 否则直接返回 data 本身。 设置 headers 对于 headers 的操作,我们会着重对 Content-Type 进行处理,在没有 Content-Type 的时候,我们应该有个默认的支持。因为 headers 属性上是大小写不敏感的,因此我们会对 Content-Type 做一个统一处理: function transformHeaders (headers) { const contentTypeKey = 'Content-Type' // Content-Type 的 key 值常量 if (isPlainObject(headers)) { Object.keys(headers).forEach(key => { if (key !== contentTypeKey && key.toupperCase() === contentTypeKey.toLowerCase()) { // 如果 key 的大写和 contentTypeKey 的大写一致,证明是同一个,这时就可以用 contentTypeKey 来替代 key 了 headers[contentTypeKey] = headers[key] delete headers[key] } }) if (!headers[contentTypeKey]) { // 如果最后发现没有 Content-Type,那我们就设置一个默认的 headers[contentTypeKey] = 'application/json;charset=utf-8' } } } // 在 function xhr 中 // 设置头部 transformHeaders(headers) Object.keys(headers).forEach(key => { if (!data && key === 'Content-Type') { delete headers[key] return } request.setRequestHeader(key, headers[key]) }) transformHeaders 函数对 headers 进行了一定程度的转化,包括为 Content-Type 提供了默认的支持,这里默认为 "application/json;charset=utf-8"。在 xhr 函数中,我们还会对headers的每一项进行判断,如果没有 data ,那我们会删除 Content-Type。同时,我们会调用 setRequestHeader 方法将 headers 属性添加到头部。 设置响应类型 if (responseType) { // 如果设置了响应类型,则为 request 设置 responseType request.responseType = responseType; } 设置超时时间 if (timeout) { // 如果设置超时时间, 则为 request 设置 timeout request.timeout = timeout; } 处理结果 // 状态变化处理函数 request.onreadystatechange = function handleLoad() { if (request.readyState !== ) return; if (request.status === ) return; // 获取响应数据 const responseData = request.responseType === "text" ? request.responseText : request.response; if (request.status >= && request.status < || request.status === ) { // 成功则 resolve 响应数组 resolve(responseData); } else { // 失败则 reject 错误原因 reject(new Error(`Request Failed with status code ${request.status}`)); } }; // 错误处理事件 request.onerror = function hadleError() { //reject 错误原因 reject(new Error('Network Error')) } // 超时处理事件 request.ontimeout = function handleTimeout() { // reject 错误原因 reject(new Error(`Timeout of ${timeout} ms exceeded`)) } 处理结果分为几个部分: 正常处理服务端响应 请求错误 请求超时 其中,正常处理服务端响应还要判断状态码,这里判断正确的是 200 至 300 之间状态码,再一个是 304 缓存。此时我们会通过 resolve 断言数据。否则,通过 reject 来断言失败原因。 (编辑:汽车网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
