一、文章简介

现在浏览器向服务器发起异步HTTP请求获取数据的库、方法有很多,比如大家所熟知的jQuery的 AJAX 以及ES6中的 fetch 、网络请求库 axios 、react中常用到的 useRequest 等。但可能大家都没有怎么使用过JS原生的网络请求 XMLHttpRequest ,本文就以 ES5 的语法根据原生 XMLHttpRequest 简单封装一个AJAX。

二、基本环境搭建

这里我们使用 nodejs+express 快速搭建后端服务:

  1. 创建 server 文件夹
  2. 进入文件夹执行命令 npm init -y (-y为所有选项选择默认设置)
  3. 使用命令 npm install express
  4. 编写 app.js ,配置 服务器相关内容
// 引入express依赖
const express = require('express');
// 获取express实例
const app = express();
// 设置app的路由信息
// 接收到'/'的路径请求时, 返回success结果
app.get('/', (req, res) => {
  res.statusCode = 200;
  res.send('success');
});
// 服务器于本地3000端口开启
app.listen(3000, () => {
  console.log('server is running at port 3000.');
});
  1. 配置package.json

如果没有安装过nodemon,可以先执行命令npm i -g nodemon

"name": "server", "version": "1.0.0", "main": "index.js", "license": "MIT", "scripts": { "dev": "nodemon ./app.js" "dependencies": { "express": "^4.18.2"
  1. 执行命令npm run dev或直接执行命令node ./app.js(注意要处于server目录下)

三、原生AJAX初体验

在这里我们简单创建一个原生AJAX用以获取服务器数据。

var xhr = new XMLHttpRequest();
// 设置xhr状态改变事件监听函数
xhr.onreadystatechange = function () {
  // 当服务器返回的状态为200且xhr对象的状态处于"请求已完成,响应已就绪状态"
  // 具体的readyState内容后续再谈
  if (xhr.status === 200 && xhr.readyState === 4) {
    // 打印服务器返回结果
    console.log(xhr.responseText);
// xhr对象向地址http://localhost:3000/建立HTTP连接
xhr.open('GET', 'http://localhost:3000/', true);
// xhr对象向服务器发送GET异步请求
xhr.send();

在这里插入图片描述
 此时我们得到结果是请求跨域被跨域策略拦截,那么我们简单聊聊跨域:跨域就是客户端的协议+域名+端口与服务器的不相同,如localhost:3000与localhost:6693因为端口不相同,因此被浏览器跨域资源共享协议所拦截。对于跨域我们会有8种解决方案,本次因为重点不在此,使用CORS的方案解决跨域。
 我们在app.js中添加上允许跨域的响应头:

app.get('/', (req, res) => {
  // ⭐添加允许访问的访问源为: '任意'
  res.setHeader('Access-Control-Allow-Origin', '*');
  // ⭐添加允许访问的请求方式为: 'GET'
  res.setHeader('Access-Control-ALlow-Method', 'get');
  res.statusCode = 200;
  res.send('success');
});

在这里插入图片描述
 我们成功解决了跨域问题,得到了服务端返回的数据success,以上就是xhr对象的初体验。

四、简单聊聊xhr的readyState

 readyState是xhr对象发送HTTP请求各阶段的状态码,一共有五种状态分别对应状态码0-4,而readyState是不能反应服务器是否正确返回信息给客户端的,需要根据xhr对象上另一个属性status来进行判断请求是否完成。

  • 0:请求未初始化
  • 1:连接已经成功建立
  • 2:服务器接收请求
  • 3:服务器处理中
  • 4:请求已完成,响应已就绪
 // 当xhr对象实例化后, readyState处于状态 0
 var xhr = new XMLHttpRequest();
 console.log('readyState at: ', xhr.readyState);
 xhr.onreadystatechange = function () {      
   // 状态2、3、4都在事件处理函数中体现
   console.log('readyState at: ', xhr.readyState);
   // 因此status = 200、readyState = 4 意为xhr对象成功获取到服务器返回数据
   if (xhr.status === 200 && xhr.readyState === 4) {        
     console.log(xhr.responseText);
 // 当xhr调用open方法后, readyState处于状态 1
 xhr.open('GET', 'http://localhost:3000/', true);    
 xhr.send();

五、初步封装XMLHttpRequest Level1标准

1. 定义工具函数utils/ajax.js

 我们使用自执行函数将私有方法封装在我们的ajax库内部,向外暴露一个对象接口,返回三个工具方法ajax、get、post,大家可以根据自己的需要添加更多方法。

var $ = (function () {
  return {
    ajax: function(options) {
    get: function(url, successCB, errorCB) {
    post: function(url, data, successCB, errorCB) {
}());

2. 利用关键函数_ajax来统一处理多种方式请求

我们的关键逻辑其实是在_ajax方法中实现的,在抛出给外界的方法中也只是内部调用了关键方法来进行AJAX请求。简单介绍一下 关键参数。url:请求地址、method:请求方式、successCB:请求成功执行的回调函数、errorCB:请求失败执行的回调函数、data:请求体

var $ = (function () {
  function _ajax(options) {
  return {
    ajax: function(options) {
      _ajax(options);
    get: function(url, successCB, errorCB) {
      _ajax({
        url: url,
        method: 'GET',
        successCB: successCB,
        errorCB: errorCB
      });
    post: function(url, data, successCB, errorCB) {
      _ajax({
        url: url,
        method: 'POST',
        data: data,
        successCB: successCB,
        errorCB: errorCB
      });
}());

3. 封装关键函数_ajax

 function _ajax(opt) {
   // 兼容性创建xhr对象
   // 其中IE5/6是不支持XMLHttpRequest的
   // IE5/6/Opera Mini是只支持微软的ActiveXObject对象发送HTTP请求
   var xhr = window.XMLHttpRequest 
     ? new XMLHttpRequest() 
     : new ActiveXObject('Microsoft.XMLHTTP');
   if (!xhr) {
     // 如果无法创建xhr对象, 返回异常信息
     throw new Error('您的浏览器不支持异步发送HTTP请求');
   // 从opt对象中获取AJAX配置信息
   var opt = opt || {},
     url = opt.url,
     method = (opt.method || 'GET').toUpperCase(),
     data = opt.data || null,
     // 请求成功的回调函数
     successCB = opt.successCB || function() {},
     // 无论请求是否成功都会执行的完成回调函数类似于finally
     completeCB = opt.completeCB || function() {},
     // 请求失败的回调函数
     errorCB = opt.errorCB || function() {},
     // 请求是否异步
     // 如果直接使用opt.async || true,传入的值为false时也会因为或运算符而得到true值
     async = '' + opt.async === 'false' ? false : true
   // 设置xhr对象的状态改变事件监听处理函数
   xhr.onreadystatechange = function () {
     if (xhr.readyState === 4) {
       // 200-300之间都为请求成功的状态码, 304为浏览器使用缓存的重定向状态码
       if ((xhr.status >= 200 && xhr.status <= 300) || xhr.status === 304) {          
         // 请求成功执行成功回调函数
         successCB(xhr.responseText);
       } else {
         // 请求失败执行失败回调函数
         errorCB(xhr.responseText);          
       // 请求完成执行完成回调函数
       completeCB(xhr.responseText);
   // 建立客户端与服务器的连接、设置相应url、方法以及是否异步
   xhr.open(method, url, async);
   // 当请求为POST请求时要设置客户端的Content-type以使服务器识别到请求是post请求的键值对形式
   // 此处也可以作为配置对象中的一个属性 -> 成为一个配置项
   method === 'POST' && xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
   // xhr对象发送请求,如果是GET请求不传入请求体、是POST请求则传入标准格式请求体
   // 如 name=zhangsan&&age=13
   xhr.send(method === 'GET' ? null : stringify(data));
 // 将对象转化为标准请求体格式
 function stringify(data) {
   var str = '';
   for (var key in data) {
     if (Object.prototype.hasOwnProperty.call(data, key)) {
       str += key + '=' + data[key] + '&';
   return str.replace(/&$/, '');

六、_AJAX小试牛刀

 在客户端页面用我们封装好的$_AJAX向我们搭建的服务器发送一条数据请求

  <script type='text/javascript' src='./utils/ajax.js'></script>
  <script type='text/javascript'>
    $.get('http://localhost:3000', function (result) {
      console.log(result);
  </script>

在这里插入图片描述
 我们也成功获取到的服务器返回的结果。

七、XMLHttpRequest Level2中新增的超时时间

 对于Level2中的超时时间,xhr上有一个属性timeout以及超时事件句柄ontimeout可以分别设置超时时间以及超时后的事件处理函数。但是, Level2版本新增的超时时间相关api具有一定的兼容性问题。

1. xhr timeout和ontimeout

在封装的AJAX中添加超时处理逻辑,
注意要一切xhr的逻辑处理都要在send函数执行之前完成

    // Level2中的超时api
    xhr.timeout = 500;
    xhr.ontimeout = function() {
      console.log('ajax timeout');

 接着我们将浏览器的网速调成Slow 3G, 重新发送AJAX请求:
在这里插入图片描述
 可以看到成功触发了xhr的超时回调函数,同时HTTP请求也被取消了。

2. 兼容性更好的自定义timeout

 通过Level2提供的Api我们能够完成对请求超时的控制,但先前也说了这种方式具有一定的兼容性问题,我们完全可以利用setTimeout手动做一个超时监听。

// xhr对象发送请求,如果是GET请求不传入请求体、是POST请求则传入标准格式请求体
// 如 name=zhangsan&&age=13
xhr.send(method === 'GET' ? null : stringify(data));
// 兼容性timeout
// 发送请求后设置超时定时器,若在设定的超时时间内未清除定时器,则判断请求超时
t = setTimeout(function() {
  timeoutCB();
  xhr.abort();
  // 以下是一种写法,可以省略
  clearTimeout(t);
  xhr = null;
  t = null;
  throw new Error('请求超时');
}, timeout);
// 同时在xhr对象状态变化的事件处理函数中对超时进行处理
 // 设置xhr对象的状态改变事件监听处理函数
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    // 当readyState处于状态4时,表示请求已完成、响应已就绪即未超时
    // 我们取消定时任务
    clearTimeout(t);        
    // 200-300之间都为请求成功的状态码, 304为浏览器使用缓存的重定向状态码
    if ((xhr.status >= 200 && xhr.status <= 300) || xhr.status === 304) {          
      // 请求成功执行成功回调函数
      successCB(xhr.responseText);
    } else {
      // 请求失败执行失败回调函数
      errorCB(xhr.responseText);          
    // 请求完成执行完成回调函数
    completeCB(xhr.responseText);
    t = null;
    xhr = null;

 我们再发送一条AJAX请求、利用浏览器的慢网速进行测试:

 <script type='text/javascript' src='./utils/ajax.js'></script>
 <script type='text/javascript'>
   $.ajax({
     method: 'GET',
     url: 'http://localhost:3000/',
     successCB: function(res) {
       console.log(res)
     timeout: 300,
     timeoutCB: function () {
       console.log('ajax timeout');
   });
 </script>

在这里插入图片描述
 可以看到,同样达成了我们想要的超时控制效果。

八、小结一下

 AJAX的内容还是相当之多的,还会遇上跨域的问题以及如何解决跨域、各种状态码的判读、强制缓存与协商缓存的区别以及如何设置、文件上传等等。还需要我慢慢去啃去学!

 上文便是本篇文章的全部内容,因为是纯业余的技术文章分享,没有过多去检查文章是否存在错误、语义不清等问题,请大伙儿见谅!如果有什么内容问题或不理解的地方欢迎大家在评论区或私信笔者一起讨论!

 我是Donp1,一个前端菜鸡,我们下次再见~

现在浏览器向服务器发起异步HTTP请求获取数据的库、方法有很多,比如大家所熟知的jQuery的AJAX以及ES6中的fetch、网络请求库axios、react中常用到的useRequest等。但可能大家都没有怎么使用过JS原生的网络请求,本文就以ES5的语法根据原生简单封装一个AJAX。我们使用自执行函数将私有方法封装在我们的ajax库内部,向外暴露一个对象接口,返回三个工具方法,大家可以根据自己的需要添加更多方法。 url: 'http://localhost:8081/soft/sts/softs', method: 'get', params: {pageIndex: 1, pageSize: 5} }).then( res => { console.log(res) err => {...
1.什么是Ajax? Ajax定义:Ajax(Asynchronous JavaScript+XML,即异步JavaScript加XML),是一种通过异步请求的方式向服务器请求数据,在无需重新刷新整个网页的情况下,能够更新部分网页的技术。 作用:更好的提升用户的体验,并减少网络数据的传输量。 2.ajax的工作原理 Ajax的工作原理相当于在用户和服务器之间加了—个间层(AJAX引擎),使用户操作与服务器响应异步化。并不是所有的用户请求都提交给服务器。像—些数据验证和数据处理等都交给Ajax...
接上文,我们已经对原生Ajax有了一个基础的了解,但是在我们日常工作,不可能每次需要用到时在用原生写一遍,所以本文针对后续工作需要,对Ajax进行封装,主要演示其封装过程,实际工作根据需求封装。 相关代码地址如下:https://source.chromium.org/chromium/chromium/src/+/main:net/socket/client_socket_pool.cc;l=25 <!DOCTYPE html> <html lang="en"> <meta charset="UTF-8"> <meta name="viewport" content="
当我们不会写后端接口来测试ajax时,我们可以使用node环境来模拟一个后端接口。 1、模拟后端接口可参考网站整站开发小例子,在打开命令窗口并转到所在项目文件夹下在命令行输入npm install express –save,安装express间件。 2、把当的app.js的内容换成 var express=require('express'); //var path=require('path'); var app=express(); //app.set('view',path.join(__dirname,'views')); //在views目录下搜索所有模板 app.en
可以使用`XMLHttpRequest`对象来发起HTTP请求并处理响应,以下是一个使用原生JavaScript封装HTTP请求的示例代码: ```javascript function httpRequest(method, url, timeout, callback) { var xhr = new XMLHttpRequest(); xhr.open(method, url, true); xhr.timeout = timeout; xhr.onload = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { callback(xhr.responseText); } else { callback(xhr.statusText); xhr.ontimeout = function() { callback('请求超时'); xhr.send(); 这个函数接受4个参数: - `method` - 请求方法,可以是 GET、POST 等。 - `url` - 请求的URL。 - `timeout` - 超时时间,单位是毫秒。 - `callback` - 回调函数,当请求完成时会调用此函数。函数会传入一个参数,即响应的数据或错误信息。 使用示例: ```javascript httpRequest('GET', 'https://jsonplaceholder.typicode.com/todos/1', 5000, function(response) { console.log(response); 在这个例子请求的URL是 `https://jsonplaceholder.typicode.com/todos/1`,请求方法是 GET,超时时间是 5000 毫秒,回调函数会在请求完成后被调用。如果请求成功,回调函数会收到响应数据,如果请求失败,则会收到错误信息。如果请求超时,回调函数会收到字符串 '请求超时'。