原生JS实现异步数据请求:AJAX与JSONP封装详解

作者:纸飞机

时间:2026-10-22

浏览:448

本文详细讲解如何在无框架依赖下使用原生JavaScript进行异步数据请求。重点解析AJAX技术核心XMLHttpRequest对象的状态机与IE兼容处理,以及利用script标签src属性实现跨域的JSONP机制。通过封装统一接口,根据dataType自动选择请求方式,提供完整的代码实现与参数说明。

文章目录

原生JS异步请求概述

项目开发中常需处理异步数据交互。若环境未引入第三方框架,则需依赖原生JavaScript实现异步请求。主要存在两种实现路径:AJAX与JSONP。本文将对这两种原生异步请求方式进行基础封装与解析。

AJAX技术实现

AJAX

AJAX(Asynchronous JavaScript and XML)技术允许在不刷新整个页面的前提下更新局部数据。其核心组件为XMLHttpRequest对象,标准请求流程包含以下关键步骤:

  • 创建XMLHttpRequest对象实例
  • 配置服务器连接参数(open)
  • 执行发送请求操作(send)
  • 监听响应数据接收状态(onreadystatechange)

AJAX核心代码封装

具体代码实现如下。


/**
* 通过JSON的方式请求
* @param {[type]} params [description]
* @return {[type]}  [description]
*/
ajaxJSON(params) {
 params.type = (params.type || 'GET').toUpperCase();
 params.data = params.data || {};
 var formatedParams = this.formateParams(params.data, params.cache);
 var xhr;
 //创建XMLHttpRequest对象
 if (window.XMLHttpRequest) {
  //非IE6
  xhr = new XMLHttpRequest();
 } else {
  xhr = new ActiveXObject('Microsoft.XMLHTTP');
 }
 //异步状态发生改变,接收响应数据
 xhr.onreadystatechange = function() {
  if (xhr.readyState == 4 && xhr.status == 200) {
   if (!!params.success) {
    if (typeof xhr.responseText == 'string') {
     params.success(JSON.parse(xhr.responseText));
    } else {
     params.success(xhr.responseText);
    }
   }
  } else {
   params.error && params.error(status);
  }
 }
 if (params.type == 'GET') {
  //连接服务器
  xhr.open('GET', (!!formatedParams ? params.url + '?' + formatedParams : params.url), true);
  //发送请求
  xhr.send();
 } else {
  //连接服务器
  xhr.open('POST', params.url, true);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  //发送请求
  xhr.send(formatedParams);
 }
},
/**
* 格式化数据
* @param {Obj}  data 需要格式化的数据
* @param {Boolean} isCache 是否加入随机参数
* @return {String}   返回的字符串
*/
formateParams: function(data, isCache) {
 var arr = [];
 for (var name in data) {
  arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));
 }
 if (isCache) {
  arr.push('v=' + (new Date()).getTime());
 }
 return arr.join('&');
}

在IE7及以上版本浏览器中,原生支持XMLHttpRequest对象,可直接通过var oAjax = new XMLHttpRequest()实例化。对于IE6及更早版本,需通过MSXML库中的ActiveXObject对象来实现XHR功能。

使用xhr对象的open方法建立服务器连接,该方法接收三个参数:请求方法、请求URL地址以及是否启用异步模式(通常设为异步)。请求方法主要分为GET与POST:GET方式将数据附加于URL提交至服务器;POST方式则将数据作为send方法的参数进行传输。

需为xhr对象绑定状态改变事件监听器onreadystatechange,用于监测readyState属性的变化。异步请求成功后,readyState值将从0递增至4,并触发相应事件。各状态码含义如下:

0 (未初始化):对象已创建,但尚未调用open方法。 1 (初始化):对象已创建,但尚未调用send方法。 2 (发送数据):send方法已调用,当前状态及HTTP头信息尚不明确。 3 (数据传送中):已接收部分响应数据,因响应完整内容及HTTP头未完全就绪,此时通过responseBody或responseText获取数据可能产生错误。 4 (完成):数据接收完毕,此时可通过responseBody或responseText获取完整响应数据。

在readystatechange事件处理逻辑中,首先判断响应是否已完全接收(readyState为4),随后验证服务器处理结果。xhr.status属性返回HTTP状态码,以2开头的状态码代表请求成功。状态码304表示从缓存读取数据;由于前述代码在每次请求时均附加随机数参数以禁用缓存,因此无需特别处理304状态。

JSONP跨域机制

JSONP

若继续使用XMLHttpRequest对象发起跨域请求,即使调用send方法,xhr状态值将始终为0,且不会触发onreadystatechange事件。此时需采用JSONP(JSON with Padding)方式进行跨域数据获取。

JSONP利用script标签支持跨域加载资源的特性实现请求。通过设置script标签的src属性指向服务器地址,服务器返回JavaScript代码片段。浏览器接收响应后直接执行该代码,其原理类似于通过script标签引用外部脚本文件。

JSONP请求由两部分构成:回调函数名与数据。回调函数名通常由客户端生成并作为参数传递给服务器端,服务器端将数据封装在该函数调用中返回。

例如,客户端创建script标签并将src设置为http://www.test.com/json/?callback=process,从而发起请求。服务端将待返回数据封装为函数参数形式返回,格式如”process({‘name':'xieyufei'})”。客户端接收到响应后,因请求源为script标签,系统会自动执行process方法并传入对应参数。

JSONP实现细节

具体代码实现如下。


/**
* 通过JSONP的方式请求
* @param {[type]} params [description]
* @return {[type]}  [description]
*/
ajaxJSONP(params) {
 params.data = params.data || {};
 params.jsonp = params.jsonp || 'callback';
 // 设置传递给后台的回调参数名和参数值
 var callbackName = 'jsonp_' + (new Date()).getTime();
 params.data[params.jsonp] = callbackName;
 var formatedParams = this.formateParams(params.data, params.cache);
 //创建script标签并插入到页面中
 var head = document.getElementsByTagName('head')[0];
 var script = document.createElement('script');
 head.appendChild(script);
 //创建jsonp回调函数 
 window[callbackName] = function(json) {
  head.removeChild(script);
  clearTimeout(script.timer);
  window[callbackName] = null;
  params.success && params.success(json);
 };
 //发送请求 
 script.src = (!!formatedParams ? params.url + '?' + formatedParams : params.url);
 //为了得知此次请求是否成功,设置超时处理
 if (params.time) {
  script.timer = setTimeout(function() {
   window[callbackName] = null;
   head.removeChild(script);
   params.error && params.error({
    message: '超时'
   });
  }, params.time);
 }
}

为script标签设置src属性会立即触发浏览器发送请求,但script标签仅能发起一次请求,无法复用。因此,每次请求完成后需移除对应的script标签。在发送请求前,需在页面全局作用域绑定回调函数,待数据请求成功时由服务器返回的代码调用该回调函数。

统一请求接口封装

总结

将上述两种异步请求方式整合,通过判断dataType参数自动选择AJAX或JSONP策略。完整封装代码展示如下。


var xyfAjax = {
 ajax: function(params) {
  params = params || {};
  params.cache = params.cache || false;
  if (!params.url) {
   throw new Error('参数不合法');
  }
  params.dataType = (params.dataType || 'json').toLowerCase();
  if (params.dataType == 'jsonp') {
   this.ajaxJSONP(params);
  } else if (params.dataType == 'json') {
   this.ajaxJSON(params);
  }
 },
 /**
 * 通过JSONP的方式请求
 * @param {[type]} params [description]
 * @return {[type]}  [description]
 */
 ajaxJSONP(params) {
  params.data = params.data || {};
  params.jsonp = params.jsonp || 'callback';
  // 设置传递给后台的回调参数名和参数值
  var callbackName = 'jsonp_' + (new Date()).getTime();
  params.data[params.jsonp] = callbackName;
  var formatedParams = this.formateParams(params.data, params.cache);
  //创建script标签并插入到页面中
  var head = document.getElementsByTagName('head')[0];
  var script = document.createElement('script');
  head.appendChild(script);
  //创建jsonp回调函数 
  window[callbackName] = function(json) {
   head.removeChild(script);
   clearTimeout(script.timer);
   window[callbackName] = null;
   params.success && params.success(json);
  };
  //发送请求 
  script.src = (!!formatedParams ? params.url + '?' + formatedParams : params.url);
  //为了得知此次请求是否成功,设置超时处理
  if (params.time) {
   script.timer = setTimeout(function() {
    window[callbackName] = null;
    head.removeChild(script);
    params.error && params.error({
     message: '超时'
    });
   }, params.time);
  }
 },
 /**
 * 通过JSON的方式请求
 * @param {[type]} params [description]
 * @return {[type]}  [description]
 */
 ajaxJSON(params) {
  params.type = (params.type || 'GET').toUpperCase();
  params.data = params.data || {};
  var formatedParams = this.formateParams(params.data, params.cache);
  var xhr;
  //创建XMLHttpRequest对象
  if (window.XMLHttpRequest) {
   //非IE6
   xhr = new XMLHttpRequest();
  } else {
   xhr = new ActiveXObject('Microsoft.XMLHTTP');
  }
  //异步状态发生改变,接收响应数据
  xhr.onreadystatechange = function() {
   if (xhr.readyState == 4 && xhr.status == 200) {
    if (!!params.success) {
     if (typeof xhr.responseText == 'string') {
      params.success(JSON.parse(xhr.responseText));
     } else {
      params.success(xhr.responseText);
     }
    }
   } else {
    params.error && params.error(status);
   }
  }
  if (params.type == 'GET') {
   //连接服务器
   xhr.open('GET', (!!formatedParams ? params.url + '?' + formatedParams : params.url), true);
   //发送请求
   xhr.send(null);
  } else {
   //连接服务器
   xhr.open('POST', params.url, true);
   xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
   //发送请求
   xhr.send(formatedParams);
  }
 },
 /**
 * 格式化数据
 * @param {Obj}  data 需要格式化的数据
 * @param {Boolean} isCache 是否加入随机参数
 * @return {String}   返回的字符串
 */
 formateParams: function(data, isCache) {
  var arr = [];
  for (var name in data) {
   arr.push(encodeURIComponent(name) + '=' + encodeURIComponent(data[name]));
  }
  if (isCache) {
   arr.push('v=' + (new Date()).getTime());
  }
  return arr.join('&');
 }
}
xyfAjax.ajax({
 url:'http://www.xieyufei.com',
 type:'get', //or post
 dataType:'json', //or jsonp
 data:{
  name:'xyf'
 },
 success: function(data){
  console.log(data)
 }
})

本文介绍了原生JavaScript实现异步数据请求的核心原理与封装方法,希望能为相关学习提供辅助,欢迎继续支持本站内容更新。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章