js原生自封装Ajax(初级)

98 阅读2分钟

js原生自封装Ajax

(function (win) {
  function myAjax ({ url = "/", method = "GET", data = {}, dataType = "json", contentType = "queryStr", withCredentials = false, success = function (data) { console.log(data) }, timeout = 0, context = window, requestHeader = {}, error = function (err) { console.error(err) } }) {
    let xhr
    //如果设置dataType为 jsonp 说明此次请求为jsonp 请求方式 直接调用jsonp封装函数
    //同时 return false 终止后续 XMLHttpRequest 创建以及ajax请求发送
    if (dataType.toLowerCase() === 'jsonp') {
      jsonpPakeage({
        url, data, success
      })
      return false
    }
    const contentTypeMap = {
      'json': 'application/json',
      'form': 'multipart/form-data',
      'text': 'text/plain',
      'queryStr': 'application/x-www-form-urlencoded'
    }
    if ('XMLHttpRequest' in window) {
      xhr = new XMLHttpRequest()
    } else {
      //IE8 以下
      xhr = new window.ActiveXObject("Microsoft.XMLHTTP");
    }
    method = method.toUpperCase()
    xhr.responseType = dataType// text json blob buffer
    xhr.withCredentials = withCredentials //请求是否带上cookie等 请求头信息
    //区别 GET POST
    if (method === 'GET') {
      //请求数据需要以queryString的形式拼接到URL上 send 方法传参 null
      url = '?' + fromatQueryString(data)
      data = null
    }
    if (contentType === 'json') {
      //如果设置  content-type 为 application/json 发送的数据需要是 字符串化的 json对象
      data = JSON.stringify(data)
    }
    if (contentType === 'queryStr') {
      data = fromatQueryString(data)
    }
    xhr.open(method, url, true)
    //如果开启了withCredentials 可以设置自定义请求头
    if (xhr.withCredentials) {
      Object.entries(requestHeader).map(([key, value]) => {
        xhr.setRequestHeader(key, value)
      })
    }
    let reqHeaderCt = contentTypeMap[contentType];
    if (!(contentType === 'form')) {
      //如果请求的content-type 是multipart 不设置requestHeader 浏览器根据请求内容自动识别
      xhr.setRequestHeader('content-type', reqHeaderCt)
    }
    xhr.send(data)
    //timeout 值单位 ms
    if (timeout !== 0) {
      let time = setTimeout(function () {
        //中断请求
        xhr.abort()
        clearTimeout(time)
      }, timeout)
    }
    xhr.onreadystatechange = function () {
      if (xhr.readyState === 4 && (xhr.status >= 200 && xhr.status <= 299)) {
        let responseData = xhr.response // "{a:1,b:2}"
        if (xhr.responseType === 'json' && isJSON(responseData)) {
          responseData = JSON.parse(responseData)
        }
        success.call(context, responseData)
      }
    }
    xhr.onerror = function (err) {
      error(err)
    }
​
    function isJSON (str) {
      if (!str) {
        return false;
      }
      try {
        JSON.parse(str)
        return true;
      } catch (err) {
        return false;
      }
    }
​
    function fromatQueryString (data) {
      return Object.entries(data).map(([key, value]) => {
        return `${key}=${value}`
      }).join('&')
    }
​
​
    function jsonpPakeage ({ url, data, success }) {
      //随机生成一个回调函数名称 fn+时间戳
      let callbackName = `fn${Date.now()}`
      //创建script标签
      let script = document.createElement('script')
      //解析data为 key=value&key=value queryString
      //'?a=1&b=2&callback=fn321341241254'
      let requestQuery = '?' + Object.entries(data).map(([key, value]) => {
        return `${key}=${value}`
      }).join('&') + `&callback=${callbackName}`
      //设置src url 拼接 query参数  http://127.0.0.1:3002/jsonp?a=1&b=2&callback=fn321341241254
      script.src = url + requestQuery
      //script标签添加到head标签内
      document.querySelector('head').appendChild(script)
​
      //给window全局对象挂一个临时回调函数 fn1604583878744
      window[callbackName] = function (data) {
        data = isJSON(data) && data || data
        //二次传递参数数据给success
        success(data)
        //数据拿到传递给success之后 window上的临时fn函数属性和 script标签就没用了
        //成功回调执行之后 删掉window上的对应临时函数
        delete window[callbackName]
        document.querySelector('head').removeChild(script)
      }
    }
​
  }
  win.ajax = myAjax
})(window)
​