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 来断言失败原因。 (编辑:汽车网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |