一、内部封装js
ajax类 初始化方法 绑定响应事件处理 设置各个header 发送请求 utils工具类 constant常量类 default配置类:基本的配置属性 index(对外暴露封装类) 给外界调用的接口
①utils工具类
// 添加url请求参数:data为已经序列化的string值,这里只需要判断url后添加?还是& const addUrlData = (url, data) => { if (!data) return ''; // 用于标识是否包含? const mark = url.includes('?') ? '&' : '?'; return `${mark}${data}`; } // 序列化:将传入的对象进行序列化并且进行编码防止有中文文字,如username=changlu&age=18 const serialize = (param) => { const results = []; for (const [key, value] of Object.entries(param)) { results.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`); } return results.join('&'); } // JSON序列化(对象=>JSON字符串,通常放置在请求体中) const serializeJSON = (data) => { return JSON.stringify(data); } export { addUrlData, serialize, serializeJSON }
②Defaults.js(默认属性方法配置类)
包含一个xhr对象的相关属性以及方法,目的是方便设置一些默认值:
属性:请求方法、请求头信息、请求体数据、内容类型、响应类型、超时时间、跨域是否携带cookie属性。
方法(监听方法):成功事件、响应码方法、报错方法、停止请求方法、超时方法。
import { HTTP_GET, CONTENT_TYPE_FROM_URLENCODED } from './constant.js'
// 默认配置类 const DEFAULTS = { //请求方法(多个,使用常量文件中的):默认GET请求 method: HTTP_GET, // 请求头携带的数据:?username=xx&age=xx params: null, // 请求体携带的数据:①对象形式。②FormData数据 data: null, // 内容类型(请求头) contentType: CONTENT_TYPE_FROM_URLENCODED, // 响应类型 responseType: '', // 超时时间 timeout: 0, // 跨域携带cookie属性 withCredentials: false, // 监听事件方法 success() { }, httpCodeError() { },//响应码错误 error() { }, abort() { }, timeOut() { } }; export default DEFAULTS;
③constant.js(常量文件)
目的:将一些常量使用文件管理起来,若是想要修改一个值直接修改配置文件即可,而不是在js中来多次修改常量。
包含如HTTP请求,内容类型以及错误响应码以及描述。
// HTTP请求 export const HTTP_GET = "GET"; // 发送内容类型 export const CONTENT_TYPE_FROM_URLENCODED = 'application/x-www-form-urlencoded'; export const CONTENT_TYPE_FROM_JSON = 'application/json'; // 封装Promise使用 export const ERROR_HTTP_CODE = 1; export const ERROR_HTTP_CODE_TEXT = 'HTTP状态码异常'; export const ERROR_REQUEST = 2; export const ERROR_REQUEST_TEXT = '请求被阻止'; export const ERROR_ABORT = 3; export const ERROR_ABORT_TEXT = '请求停止'; export const ERROR_TIMEOUT = 4; export const ERROR_TIMEOUT_TEXT = '请求超时';
④Ajax.js(自定义Ajax类,用来进行初始化执行一系列操作)
其中封装了如下的步骤在构造器中:
1、构造器传入url、options初始化。
2、监听事件绑定,如success、error、errorhttpcode、abort、timeout等等,方便外部调用者直接外部写回调函数。
3、open()初始化。(请求方法与请求参数是否拼接)
4、特殊属性定制设置,如ResponseType、timeout、WithCredentials。(这部分是将options配置对象中的值真正赋给xhr对象)
5、发送请求send()。考虑多种请求①发送GET请求,其他为POST请求。②data为FormData类型。③data为x-www-form-urlencoded类型传递。④data为JSON类型传递。⑤其他类型传递。
// 导入配置类 import DEFAULTS from './default.js'; // 导入工具类 import { addUrlData, serialize, serializeJSON } from './utils.js'; // 导入常量类 import { HTTP_GET, CONTENT_TYPE_FROM_URLENCODED, CONTENT_TYPE_FROM_JSON } from "./constant.js"; class Ajax { constructor(url, options) { this.url = url; this.options = Object.assign({}, DEFAULTS, options); // 初始化方法 this.init(); } // 初始化 init() { const xhr = new XMLHttpRequest(); this.xhr = xhr; // 绑定事件 this.bindEvents(); xhr.open(this.options.method, this.url + this.addParam(), true); // 设置属性到xhr中 // 设置响应类型 this.setResponseType(); // 设置超时时间 this.setTimeout(); // 设置跨域是否携带cookie this.setWithCredentials(); // 发送请求 this.send(); } // 绑定响应事件处理程序 bindEvents() { const xhr = this.xhr; const { success, httpCodeError, error, abort, timeOut } = this.options; // 绑定监听success事件(通过监听onload事件) xhr.addEventListener("load", () => { if (this.ok()) { success(xhr.response, xhr); } else { httpCodeError(xhr.response, xhr); } }, false); // error xhr.addEventListener("error", () => { error(xhr); }, false); // abort xhr.addEventListener("abort", () => { abort(xhr); }, false); // timeout xhr.addEventListener("timeout", () => { timeOut(xhr); }, false); } // 判断当前状态码是否为成功 ok() { const xhr = this.xhr; return (xhr.status >= 200 && xhr.status <= 299) || xhr.status == 304; } // 在请求地址上添加数据 addParam() { const { params } = this.options; //无请求参数 if (!params) return ''; // 考虑到传入的url地址是否已经带有?,所以传入URL以及params return addUrlData(this.url, serialize(params)); } // 设置响应类型 setResponseType() { this.xhr.responseType = this.options.responseType; } // 设置超时时间 setTimeout() { const { timeout } = this.options; if (timeout > 0) { this.xhr.timeout = timeout; } } // 设置是否跨域携带cookie setWithCredentials() { const { withCredentials } = this.options; if (withCredentials) { this.xhr.withCredentials = withCredentials; } } // 发送请求 send() { const xhr = this.xhr; //测试是否需要使用send()来发送数据 if (!this.isSendData()) { xhr.send(null); return; } // 处理data数据 let resultData;//最终需要发送的data数据 const { data } = this.options; // 是否为FormData格式数据 if (this.isFormData(data)) { resultData = data; } else if (this.isFormURLEncodedData()) { // console.log("data=>", data); // 发送类型为application/x-www-form-urlencoded数据 resultData = serialize(data); this.xhr.setRequestHeader('Content-Type', CONTENT_TYPE_FROM_URLENCODED); } else if (this.isJSONData()) { // 发送类型为application/json数据 resultData = serializeJSON(data); this.xhr.setRequestHeader('Content-Type', CONTENT_TYPE_FROM_JSON); } else { this.setContentType(); resultData = data; } xhr.send(resultData); } // 是否想要使用send()发送数据 isSendData() { // 根据method以及data来确定 const { method, data } = this.options; // data没有值或者请求方法为GET返回false if (!data) return false; if (method.toLowerCase() === HTTP_GET.toLowerCase()) return false; return true; } // 是否data值为FormData类 isFormData() { return this.options.data instanceof FormData; } // 是否发送 application/x-www-form-urlencoded 格式的数据 isFormURLEncodedData() { return this.options.contentType .toLowerCase() .includes(CONTENT_TYPE_FROM_URLENCODED); } // 是否发送 application/json 格式的数据 isJSONData() { return this.options.contentType .toLowerCase() .includes(CONTENT_TYPE_FROM_JSON); } // 发送其他类型的数据 setContentType(contentType = this.options.contentType) { if (!contentType) return; this.xhr.setRequestHeader("Content-Type", contentType); } // 获取xhr对象 getXHR() { return this.xhr; } } export default Ajax;
二、外部封装(提供给外界使用的API,含测试)
其实本质就是对Ajax类进行二次封装出API来提供为外界使用。
2.1、Ajax原生封装
如ajax、get、getJSON、post方法,这些名称都十分鲜明对应着不同的请求方式:
import Ajax from './ajax.js' const ajax = (url, options) => { return new Ajax(url, options).getXHR(); } const get = (url, options) => { return ajax(url, { ...options, method: 'GET' }); } const getJSON = (url, options) => { return ajax(url, { ...options, method: 'GET', responseType: 'json' }); } const post = (url, options) => { return ajax(url, { ...options, method: 'POST' }); } export { ajax, get, getJSON, post }
测试
</div><div> import { ajax, get, getJSON, post } from '../index(原生封装ajax).js'</div><div> const url = 'https://www.imooc.com/api/http/search/suggest?words=js';</div><div> const xhr = ajax(url, {</div><div> method: 'POST',</div><div> data: {</div><div> username: 'changlu',</div><div> age: 18</div><div> },</div><div> timeout: 10,</div><div> responseType: 'json',</div><div> success(response) {</div><div> console.log(response);</div><div> },</div><div> error(xhr) {</div><div> console.log("error");</div><div> },</div><div> abort() {</div><div> console.log("abort");</div><div> },</div><div> timeOut() {</div><div> console.log("timeout");</div><div> }</div><div> });</div><div> // xhr.abort();</div><div>
2.2、Ajax进阶封装Promise
相较于原生封装有什么优势呢?
使用原生封装我们需要在传入对象中写一些回调函数如success、error、abort、timeout等等,这样的话也会造成代码量过多的情况。而使用Promise不再需要编写这些额外回调函数,交由Promise,开发者只需要关注then()与catch()方法即可:success=>then,error、abort、timeout=>catch捕获!!!
下面使用Promise进行封装,实际上只需要在Ajax原生封装上进行封装Promise即可:
import Ajax from './ajax.js' // 引入错误码以及错误描述常量 import { ERROR_HTTP_CODE, ERROR_REQUEST, ERROR_TIMEOUT, ERROR_ABORT, ERROR_HTTP_CODE_TEXT, ERROR_REQUEST_TEXT, ERROR_TIMEOUT_TEXT, ERROR_ABORT_TEXT } from './constant.js'; const ajax = (url, options) => { let xhr; const p = new Promise((resolve, reject) => { // 内部对几个方法再次进行封装 xhr = new Ajax(url, { ...options, ...{ //响应成功调用then()方法 success(response) { resolve(response); }, //其他情况统一传递给catch()捕捉,传递type以及text属性,分为为错误码以及错误描述 httpCodeError(status) { reject({ type: ERROR_HTTP_CODE, text: `${ERROR_HTTP_CODE_TEXT}:${status}` }) }, error() { reject({ type: ERROR_REQUEST, text: ERROR_REQUEST_TEXT }) }, abort() { reject({ type: ERROR_ABORT, text: ERROR_ABORT_TEXT }) }, timeOut() { reject({ type: ERROR_TIMEOUT, text: ERROR_TIMEOUT_TEXT }) } } }).getXHR();; }); //将xhr对象赋值给promise对象,为方便开发者可以调用到它自己额外做一些操作等 p.xhr = xhr; p.ERROR_HTTP_CODE = ERROR_HTTP_CODE; p.ERROR_REQUEST = ERROR_REQUEST; p.ERROR_TIMEOUT = ERROR_TIMEOUT; p.ERROR_ABORT = ERROR_ABORT; return p;//返回Promise对象 } const get = (url, options) => { return ajax(url, { ...options, method: 'GET' }); } const getJSON = (url, options) => { return ajax(url, { ...options, method: 'GET', responseType: 'json' }); } const post = (url, options) => { return ajax(url, { ...options, method: 'POST' }); } export { ajax, get, getJSON, post }
测试
<script type="module"> import { ajax, get, getJSON, post } from '../index(2封装ajax进阶Promise).js'; const url = 'https://www.imooc.com/api/http/search/suggest?words=js'; const p = getJSON(url, { params: { username: 'changlu', age: 18 }, timeout: 10 }); // 获取到xhr console.log(p.xhr); // 执行abort()来进行测试 // p.xhr.abort(); //统一这里来进行结果的处理!!! p.then((response) => { console.log(response); }).catch(error => { console.log("type=>" + error.type + ",text=>" + error.text); }); </script>