Ajax的原生js封装

    身为一个前端开发者,对ajax这一块需要非常的熟悉,而且现在一直在提的前后端分离,ajax起着非常重要的作用,本文将简单介绍一下ajax,并附上原生js的封装

    what is Ajax?

     Ajax即 Asynchronous JavaScript and XML ,翻译过来就是异步的JavaScript和XML,个人的理解即是一种与后端交互且不需要刷新整个页面的技术,以前前端要发数据给后端需要使用form表单来提交数据,每一次的提交就要刷新整个页面,而使用ajax,则可以在不刷新页面的同时发送数据给后端,并从后端接收数据,举个最常见的例子,登陆注册,不使用ajax的话,如果要把数据传给后端做验证,需要通过form表单,输入用户名,密码,然后提交,然后整个页面刷新,如果登入失败则返回登入页面,重新登陆,这时你以前在输入框里输入的账号和密码都一并消失(因为你刷新页面了),而如果使用ajax来对后端进行交互的话,则简单的多了,输入用户名,密码,然后利用ajax将数据传给后端,后端验证完数据,在给ajax一个响应,登陆是否成功,整个过程没有页面的刷新,除此之外使用ajax还可以动态验证账号是否存在,密码是否正确等等,当然这些的前端部分都是通过js来实现的。

    ajax的最简单的理解就是请求和获得响应,即我前端使用ajax以GET或POST的方式向服务端请求数据,请求成功后服务端响应数据,然后前端接收到响应。

    ajax的原生封装

    不得不说关于ajax的库或npm包多的数不胜数,比如jquery,axios等,个人觉得axios比较好用,毕竟人家是专门做ajax的,不过我们还是得知道那些库的基本原理,所以我们来简单的实现一个自己的ajax库,首先所有的操作都依赖于XMLHttpRequest对象,该对象的基本api请看这里:https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest

来看一个基本的例子,

    针对GET请求,其中url是你请求数据的路由

var xhr = new XMLHttpRequest();
//open(请求方法,请求的路由,是否异步,用户名,密码)
xhr.open("GET",url,true);
//可以使用timeout方法设置超时,即超过该时间没有取得响应则取消这次的请求
xhr.timeout = 2000;//
xhr.send();
//监听响应,onreadystatechange是一个事件
xhr.onreadystatechange = function () {
  //这里的this就是上面的xhr
  if(this.readyState === 4 && this.status === 200) {//这代表响应成功
    console.log(this.responseText);//所有响应的数据都在responseText里,这是个string类型
  }
};

  对于get请求如果想要传数据给后端的话,需要将数据添加到url末尾,即url?data1=aaa&data2=bbb这种形式

  针对POST请求

var xhr = new XMLHttpRequest();
//open(请求方法,请求的路由,是否异步,用户名,密码)
xhr.open("POST",url,true);
//可以使用timeout方法设置超时,即超过该时间没有取得响应则取消这次的请求
xhr.timeout = 2000;
//这里需要设置请求头,不然后端可能不认识
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
//使用send发送数据,数据格式为data1=5&data2=3
xhr.send("name=dpf&age=8");
//监听响应,onreadystatechange是一个事件
xhr.onreadystatechange = function () {
  //这里的this就是上面的xhr
  if(this.readyState === 4 && this.status === 200) {//这代表响应成功
    console.log(this.responseText);//所有响应的数据都在responseText里,这是个string类型
  }
};

有了这些基础,我们就可以自己封装一个ajax库了

ajax.js

!function (window, factory) {
  //将factory的返回值(ajax函数)加到全局对象的ajax属性里
  window.ajax = factory();
}(this, function () {
  //将对象转换为querystring,例如{a:1,b:2} => a=1&b=2
  function queryString(object) {
    var string = "";
    if (!isObject(object)) return string
    for (var key in object) {
      if (object.hasOwnProperty(key)) {
        if (typeof object[key] === "object") {
          string += key + "=" + JSON.stringify(object[key]) + "&"
        } else {
          string += key + "=" + object[key] + "&"
        }
      }
    }
    return string.substr(0, string.length - 1)
  }

  //判断是否为string
  function isString(str) {
    return typeof str === "string"
  }

  //判断是否为对象
  function isObject(obj) {
    return obj && typeof obj === "object" && Object.prototype.toString.call(obj) === "[object Object]"
  }

  //判断是否为函数
  function isFunction(fn) {
    return typeof fn === 'function';
  }

  //ajax函数url: 路由, config:配置对象, callback回调函数
  function ajax(url, config, callback) {
    if (!isString(url) || !isObject(config)) throw new Error("arguments error");
    var data = config.data || null;             //发给后端的数据
    var method = config.method || "GET";        //方法GET or POST
    var async = config.async || true;           //是否异步
    var dataType = config.dataType || "json";   //期望接收的数据类型
    var timeout = config.timeout || Infinity;   //超时
    var isGet = Boolean(method === "GET");      //是否为GET
    var isPost = Boolean(method === "POST");    //是否为POST
    var qsData = queryString(data);             //将传入的数据对象转换为字符串形式

    var xhr = new XMLHttpRequest();

    url = ajax.baseUrl ? ajax.baseUrl + url : url;//路由为基路由与url相加
    xhr.type = dataType;                          //给xhr设置个type属性
    xhr.timeout = timeout;                        //设置超时
    xhr.open(method,
      isGet && qsData ? (url += "?" + qsData) : url //如果是GET方法且data不为null,则将data对象转换为字符串形式然后加到url末尾
    );
    isPost && xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); //如果是POST方法,执行&&后面的代码
    xhr.send(isPost ? qsData : null);   //如果是POST方法,将数据发送出去
    xhr.onreadystatechange = function () {
      //得到响应
      if (this.readyState === 4 && this.status === 200) {
        switch (this.type) {
          case "json": {
            isFunction(callback) && callback(JSON.parse(this.responseText))
            break
          }
          case "text": {
            isFunction(callback) && callback(this.responseText)
            break
          }
          case "xml": {
            isFunction(callback) && callback(this.responseXML)
            break
          }
          default: {
            break
          }
        }
      }
    }
  }

  //便捷方法
  ajax.get = function (url, data, callback) {
    return this(url, {
      method: "GET",
      async: true,
      data: data,
      dataType: "json"
    }, isFunction(callback) && callback)
  };
  //便捷方法
  ajax.post = function (url, data, callback) {
    return this(
      url, {
        method: "POST",
        async: true,
        data: data,
        dataType: "json"
      },
      isFunction(callback) && callback
    )
  };
  //设置基路由
  ajax.baseUrl = "";
  return ajax
});

使用node.js编写个后端来测试ajax函数

扫描二维码关注公众号,回复: 5347338 查看本文章

test.js

const http = require("http");
const queryString = require("querystring");
const url = require("url");
const app = http.createServer((req, res) => {});
//处理post数据
async function getPostData(req) {
  let data = "";
  for await (let chunk of req) {//最新的异步迭代器
    data += chunk;
  }
  return queryString.parse(data);
}

app.on("request", async (req, res) => {
  let method = req.method;
  let pathname = url.parse(req.url).pathname;
  res.writeHead(200, {"Content-type": "application/json","Access-Control-Allow-Origin":"*"});
  switch (pathname) {
    case "/get/api": {
      if (method === "GET") {
        let reqData = queryString.parse(url.parse(req.url).query);
        res.end(JSON.stringify({err: false, reqData, data: [1, 2, 4]}));
      }
      break
    }
    case "/post/api": {
      if (method === "POST") {
        let reqData = await getPostData(req);
        res.end(JSON.stringify({err: false, reqData, data: [2, 2, 2, 2, 2]}))
      }
      break
    }
    default : {
      break
    }
  }

});
app.listen(3000, () => console.log("listen in 3000"));

做一个简单的测试

a.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Title</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="ajax.js"></script>
</head>
<body>
</body>
<script>
  !function (window) {
    var ajax = window.ajax;
    ajax.baseUrl = "http://localhost:3000";
    ajax("/get/api", {
      method: "GET",
      async: true,
      data: {name: "dpf", sex: "max"},
      dataType: "json"
    }, console.log);
    ajax.get("/get/api", {name: "aaa", sex: "ppp"}, console.log);
    
    setTimeout(function () {
      ajax("/post/api", {
        method: "POST",
        data: {name: "dhq", sex: "man", age: 10}
      }, console.log);
      ajax.post("/post/api", {name: "aaa", sex: "fuxnn"}, console.log);
    }, 1000);
      
  }(this)
</script>
</html>

浏览器打开a.html,打开控制台,可以看到响应的数据 

 上面是使用了回调的形式来接收响应的数据,还可使用es6的Promise对象,生成器,async/await来写,其实原理都一样,都是使用XMLHttpRequest对象,更多的写法可以参考我的这篇博客:https://mp.csdn.net/postedit/83105098(JS实现异步的5种方式)

也可以使用es6的class语法封装成一个类,下面是将上面的ajax代码变为一个类的代码

ajax_class.js

!function (window) {
  function queryString(object) {
    let string = "";
    if (object === null || typeof(object) !== "object") return string;
    Object.keys(object).forEach(key => {
      if (typeof object[key] === "object") {
        string += key + "=" + JSON.stringify(object[key]) + "&"
      } else {
        string += key + "=" + object[key] + "&"
      }
    });
    return string.substr(0, string.length - 1)
  }
  class Ajax {
    constructor({baseUrl = "", timeout = Infinity,async = true} = {}) {
        Object.assign(this,{baseUrl,timeout,async});
        this._xhr = new XMLHttpRequest();
    }
    static create({baseUrl = "", timeout = Infinity,async = true} = {}){
      return new Ajax({baseUrl,timeout,async})
    }
    ajax(url,{data = null, method = "GET", dataType = "json", timeout = null} = {}) {
      //返回一个Promise对象
      return new Promise((resolve, reject) => {
        this._xhr.timeout = timeout || this.timeout;
        let isGET = Boolean(method === "GET");
        let isPOST = Boolean(method === "POST");
        let qsData = queryString(data);
        url = this.baseUrl + url;
        this._xhr.open(method,
          isGET && qsData ? `${url}?${qsData}` : url,
          this.async
        );
        isPOST && (this._xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"));
        this._xhr.send(isPOST ? qsData : null);
        this._xhr.onreadystatechange = () => {
          if (this._xhr.readyState === 4) {
            if (this._xhr.status === 200) {
              switch (dataType) {
                case "json":{
                  resolve(JSON.parse(this._xhr.responseText));
                  break
                }
                case "text":{
                  resolve(this._xhr.responseText);
                  break
                }
                case "xml":{
                  resolve(this._xhr.responseXML);
                  break
                }
              }
            } else {
              reject(new Error("it has error"))
            }
          }
        }
      })
    }
    get(url,data = {}) {
      return this.ajax(url,{data})
    }
    post(url,data = {}) {
      return this.ajax(url,{data,method:"POST"})
    }
  }
  window.Ajax = Ajax;
}(this);

使用的话只需这样就ok了

async function main() {
       const ajax = Ajax.create({baseUrl:"http://localhost:3000"});
       let data = await ajax.get("/get/api",{name:"dpf",sex:"AAAA"});
       console.log(data);
}
main();

猜你喜欢

转载自blog.csdn.net/daydream13580130043/article/details/85013952