现在流行的 Ajax 库,都是基于 Promise 实现的,如:axios。所以基于 Promise 封装一个简单的 Ajax 库,来理解其原理。

参数设置

options: [Object]

  • type: [String] 请求方式,默认 get

  • url: [String] 请求地址

  • data: [Object] 发送的数据

  • dataType: [String] 数据类型,json/xml/text,默认 json

  • cache: [Boolean] 是否缓存,默认不缓存

默认都是异步操作,不支持同步

整体结构

使用一个闭包封装,然后通过挂载到 window 来使用,借用了 JQuery 的思想

  1. ;(function(window) {
  2. function ajaxPromise(options) {
  3. //...
  4. }
  5. window.ajax = ajaxPromise;
  6. })(window)

构造函数

由于这个方法基于 Promise 来管理 Ajax 操作,所以,构造函数必定返回一个 Promise 实例。

在这个 Promise 实例中的回调函数中执行 Ajax 操作,把参数的处理,放到外面。

  1. function ajaxPromise(opts) {
  2. //=> 处理各种参数
  3. return new Promise(function (resolve, reject) {
  4. //=> 这里执行原生 Ajax
  5. let xhr = new XMLHttpRequest();
  6. xhr.open(type, url); // 默认异步
  7. xhr.onreadystatechange = function () {
  8. if (xhr.readyState === 4 && /^2\d{2}|304/.test(xhr.status)) {
  9. //=> 成功
  10. // 处理数据类型
  11. //...
  12. resolve(data);
  13. }
  14. if (xhr.readyState === 4 && /^[45]\d{2}/.test(xhr.status)) {
  15. //=> 失败
  16. reject(xhr);
  17. }
  18. };
  19. //=> 设置请求头,使得后台接受的参数是 form data,而不是字符串
  20. xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;charset=utf-8');
  21. xhr.send(data);
  22. })
  23. }

参数的处理与前面封装的 Ajax 库一样,这里不再赘述。

这里只需要简单的在 Ajax 请求成功后,执行 resolve,失败后执行 reject 即可。

代码

  1. ; (function (window) {
  2. function ajaxPromise(opts) {
  3. let {
  4. url,
  5. type = 'get',
  6. data = {},
  7. dataType = 'json',
  8. cache = false
  9. } = opts;
  10. //=> 处理 data
  11. if (typeof data === 'object') {
  12. let str = '';
  13. for (let k in data) {
  14. if (data.hasOwnProperty(k)) {
  15. str += `${k}=${data[k]}&`;
  16. }
  17. }
  18. data = str.slice(0, str.length - 1);
  19. }
  20. // 判断是否是 get 类型请求
  21. if (/^(GET|DELETE|HEAD|TRACE|OPTIONS)$/i.test(type)) {
  22. let char = '';
  23. if (url.indexOf('?') === -1) {
  24. char = `?`;
  25. } else {
  26. url = url.replace(/&$/, '');
  27. char = `&`;
  28. }
  29. url += `${char}${data}`;
  30. data = null;
  31. }
  32. //=> 处理 cache
  33. if (/^GET$/i.test(type) && cache === false) {
  34. //=> url 末尾追加时间戳
  35. let char = '';
  36. if (url.indexOf('?') === -1) {
  37. char = `?`;
  38. } else {
  39. url = url.replace(/&$/, '');
  40. char = `&`;
  41. }
  42. url += `${char}_=${+(new Date())}`;
  43. this.url = url;
  44. }
  45. return new Promise(function (resolve, reject) {
  46. //=> 这里执行原生 Ajax
  47. let xhr = new XMLHttpRequest();
  48. xhr.open(type, url); // 默认异步
  49. xhr.onreadystatechange = function () {
  50. if (xhr.readyState === 4 && /^2\d{2}|304/.test(xhr.status)) {
  51. //=> 成功
  52. // 处理数据类型
  53. let data = xhr.responseText;
  54. switch (dataType.toLowerCase()) {
  55. case 'json':
  56. data = JSON.parse(data);
  57. break;
  58. case 'xml':
  59. data = xhr.responseXML;
  60. case 'text':
  61. break;
  62. default:
  63. }
  64. resolve(data);
  65. }
  66. if (xhr.readyState === 4 && /^[45]\d{2}/.test(xhr.status)) {
  67. //=> 失败
  68. reject(xhr);
  69. }
  70. };
  71. //=> 设置请求头,使得后台接受的参数是 form data,而不是字符串
  72. xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;charset=utf-8');
  73. xhr.send(data);
  74. })
  75. }
  76. window.ajaxPromise = ajaxPromise;
  77. }) (window);