Skip to content

自实现之Ajax库 - XMLHttpRequest实现 #1

Description

@littlewin-wang

自入行前端一来各种项目里有很多 Ajax 应用案例,大多数情况下都考虑了用 axios 等库来实现需求,直到打包的时候才 WTF,为什么一个简简单单的 Ajax 需求要耗费我几十 KB 的打包空间?

本文先以兼容性较好的 XMLHttpRequest (Level 2) 标准入手,一步步打造符合自己使用需求的Ajax库。

背景知识

首先,请大家阅读上述两篇文章,掌握XMLHttpRequest的常用属性和方法。
接下来的阅读我将默认您已经会用XMLHttpRequest啦。

兼容性

1749021644-569b9fb8d3b4a_articlex

一些后期需要 polyfill 的功能点

  1. IE8/IE9、Opera Mini 完全不支持xhr对象
  2. IE10/IE11部分支持,不支持 xhr.responseType为json
  3. 部分浏览器不支持设置请求超时,即无法使用xhr.timeout
  4. 部分浏览器不支持xhr.responseType为blob

实现思路

配置参数

首先确定需要参数化的一些配置属性

  • url: 目标地址, 必须
  • headers: 请求头属性对象 - {header_name: header_value, ...}
  • body:
    • string化的参数序列 ('application/x-www-form-urlencoded'较为常用,备选)
    • FormData (未设置 Content-Type 的默认值)
  • method: 'GET', 'POST', etc.
  • cors: If your using cross-origin, you will need this true for IE8-9

回调函数参数

确定回调函数参数

  • statusCode
    • null or 0,考虑异常
  • response
    • 根据 Content-Type 输出对象
    • 或者中断后输出 "Abort"
    • 或者超时后输出 "Timeout"
    • 或者其他错误输出 "Error"

代码

// xhr合法属性
var reqfields = [
  'responseType', 'withCredentials', 'timeout', 'onprogress'
]

const ajax = function (params, callback) {
  var headers = params.headers || {}
    , body = params.body
    , method = params.method || (body ? 'POST' : 'GET')
    , called = false

  // xhr对象generate
  var req = getRequest(params.cors)

  // 回调函数规范输出
  function cb(statusCode, responseText) {
    return function () {
      if (!called) {
        callback(req.status === undefined ? statusCode : req.status,
                 req.status === 0 ? "Error" : (req.response || req.responseText || responseText),
                 req)
        called = true
      }
    }
  }

  req.open(method, params.url, true)

  var success = req.onload = cb(200)
  req.onreadystatechange = function () {
    if (req.readyState === 4) success()
  }
  req.onerror = cb(null, 'Error')
  req.ontimeout = cb(null, 'Timeout')
  req.onabort = cb(null, 'Abort')

  if (body) {
    // 却省设置 FormData => 'application/x-www-form-urlencoded' 
    if (!window.FormData || !(body instanceof window.FormData)) {
      setDefault(headers, 'Content-Type', 'application/x-www-form-urlencoded')
    }
  }

  // xhr对象属性配置
  for (var i = 0, len = reqfields.length, field; i < len; i++) {
    field = reqfields[i]
    if (params[field] !== undefined)
      req[field] = params[field]
  }

  // xhr对象header配置
  for (var field in headers)
    req.setRequestHeader(field, headers[field])

  req.send(body)

  return req
}

function getRequest(cors) {
  // IE 8 and 9 polifill
  if (cors && window.XDomainRequest && !/MSIE 1/.test(navigator.userAgent))
    return new XDomainRequest
  if (window.XMLHttpRequest)
    return new XMLHttpRequest
}

function setDefault(obj, key, value) {
  obj[key] = obj[key] || value
}

Demo

ajax({
  method: 'GET',
  responseType: 'json',
  url: 'https://cnodejs.org/api/v1/topics',
  headers: {
    'Content-Type': 'application/json'
  }
}, function (code, responseText) {
  console.log(code)
  console.log(responseText)
})
{
"success": true,
"data": [ ... ] // 40 Items
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions