热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:重拾Ajax

篇首语:本文由编程笔记#小编为大家整理,主要介绍了重拾Ajax相关的知识,希望对你有一定的参考价值。本来想专门学习一个FetchAPI的相关知识,然后从XMLHttpR

篇首语:本文由编程笔记#小编为大家整理,主要介绍了重拾Ajax相关的知识,希望对你有一定的参考价值。


本来想专门学习一个Fetch API的相关知识,然后从XMLHttpRequest对象开始看起,看着看着,突然发现自己每天都在使用的ajax竟然还有好多知识点都不熟悉,细思极恐,于是乎从MDN到W3C文档,各种百度之后,终于简单整理了一份有关于ajax的学习笔记,一方面加深印象,另一方面便于查阅,如有批漏,敬请指正。


发展历程

XMLHttpRequest一开始只是微软浏览器提供的一个接口,后来各大浏览器纷纷效仿也提供了这个接口,再后来W3C对它进行了标准化,提出了XMLHttpRequest标准。XMLHttpRequest标准又分为Level 1和2008年的Level 2。

传统的Ajax请求,使用的是XMLHttpRequest对象,如果是IE浏览器的IE6或以下的版本中,使用的是ActiveXObject对象。

XMLHttpRequest Level 1 主要存在以下缺点:



  • 受同源策略的限制,不能发送跨域请求;

  • 不能发送二进制文件(如图片、视频、音频等),只能发送纯文本数据;

  • 在发送和获取数据的过程中,无法实时获取进度信息,只能判断是否完成;

XMLHttpRequest Level 2 中新增了以下功能:



  • 可以发送跨域请求,在服务端允许的情况下;

  • 支持发送和接收二进制数据;

  • 新增formData对象,支持发送表单数据;

  • 发送和获取数据时,可以获取进度信息;

  • 可以设置请求的超时时间;


编写方式



  1. 创建 XMLHttpRequest 的实例

  2. 指定回调函数处理成功和失败等

  3. 调用两个方法 open() 与 send() 分别用于建立连接和发送数据


Leve1的请求方式









var xhr = creatXHR();

xhr.Onreadystatechange= readyCallback;

xhr.open();

xhr.send();

 

function creatXHR(){

if(window.XMLHttpRequest) return new XMLHttpRequest();

var created = false;

if(window.ActiveXObject){

var versiOns= [‘MSXML2.XMLHttp.6.0‘, ‘MSXML2.XMLHttp.3.0‘, ‘MSXML2.XMLHttp‘];

while(versions.length){

try{

var oXhr = new ActiveXObject(versions.shift());

created = true;

return xhr;

}catch(e){}

}

}

if(!created){

throw new Error(‘您的浏览器版本过低,无法创建异步对象,请升级您的浏览器‘)

}

}

function readyCallback(){

if(this.readyState === 4){

if(this.status >= 200 && xhr.status <300 || xhr.status == 304){

console.log(this.responseText);

}else{

console.log(this.status, this.statusText)

}

}

}


Leve2的请求方式

跨域请求一张图片,并打印进度









var xhr = new XMLHttpRequest();

xhr.timeout=3000;

xhr.respOnseType= ‘blob‘;

 

xhr.Onloadstart= onLoadStart;

xhr.Onload= onLoad;

xhr.Onloadend= onLoadEnd;

xhr.Onerror= onError;

xhr.Onabort= onAbort;

xhr.Ontimeout= onTimeout;

xhr.Onprogress= onProgress;

 

xhr.open(‘post‘,‘http://7xi480.com1.z0.glb.clouddn.com/ChMkJlbKzE6IPzkoABFpT19gRYgAALI0wOYts8AEWln666.jpg‘)

xhr.send(data);

 

function onLoad(e){

console.log(‘成功‘);

document.getElementById(‘imgBox‘).src = URL.createObjectURL(xhr.response);

}

function onError(e){

console.log(‘失败:‘, xhr.statusText)

}

function onAbort(e){

console.log(‘异步请求已经取消‘)

}

function onProgress(e){

if(e.total>0) console.log(Math.ceil(e.loaded / e.total *100));

}

function onTimeout(){

console.log(‘超时‘);

}

function onLoadStart(e){

console.log(‘开始‘);

}

function onLoadEnd(e){

console.log(‘完成‘);

}

 


同步和异步请求

open的第三个参数async,表示是否是异步请,默认是true
如果aync是false,发送同步请求,那么请求的配置会有一些限制:



  • xhr.timeout 必须为 0

  • xhr.withCredentials 必须为 false

  • xhr.responseType 必须为""(注意置为"text"也不允许)

  • 若上面任何一个限制不满足,都会抛错,而对于异步请求,则没有这些参数设置上的限制。

页面中应该尽量避免使用sync同步请求:
因为我们无法设置请求超时时间(xhr.timeout为0,即不限时)。在不限制超时的情况下,有可能同步请求一直处于pending状态,服务端迟迟不返回响应,这样整个页面就会一直阻塞,无法响应用户的其他交互。


指定响应的数据类型

responseType是xhr level 2新增的属性,用来指定xhr.response的数据类型,目前还存在些兼容性问题,可取值如下:



  • "" 如果没有值,默认返回String

  • "text" String字符串

  • "document" Document对象 希望返回 XML 格式数据时使用

  • "json" Javascript 对象 存在兼容性问题,IE10/IE11不支持

  • "blob" Blob对象

  • "arrayBuffer" ArrayBuffer对象


请求状态

xhr.readyState 只读属性,用于追踪Ajax请求过程的不同状态,有以下几个取值:



  • 0 UNSENT (初始状态,未打开) 此时xhr对象被成功构造,open()方法还未被调用

  • 1 OPENED (已打开,未发送) open()方法已被成功调用,send()方法还未被调用。注意:只有xhr处于OPENED状态,才能调用xhr.setRequestHeader()和xhr.send(),否则会报错

  • 2 HEADERS_RECEIVED (已获取响应头) send()方法已经被调用, 响应头和响应状态已经返回

  • 3 LOADING (正在下载响应体) 响应体(response entity body) 正在下载中,此状态下通过xhr.response可能已经有了响应数据

  • 4 DONE (整个数据传输过程结束) 整个数据传输过程结束,不管本次请求是成功还是失败









    xhr.Onreadystatechange= function () {

    switch(xhr.readyState){

    case 1: //OPENED

    //do something

    break;

    case 2://HEADERS_RECEIVED

    //do something

    break;

    case 3://LOADING

    //do something

    break;

    case 4://DONE

    //do something

    break;

    }




发送的数据类型

xhr.send(data) 的参数data可以是以下几种类型:



  • ArrayBuffer

  • Blob

  • Document

  • DOMString

  • FormData

  • null

xhr.send(data)中data参数的数据类型会影响请求头部content-type的默认值:



  • Document 类型,同时也是html Document类型,则content-type默认值为text/html;charset=UTF-8;否则为application/xml;charset=UTF-8

  • DOMString 类型,content-type默认值为text/plain;charset=UTF-8

  • FormData 类型,content-type默认值为multipart/form-data; boundary=[xxx]

  • 其他类型,则不会设置content-type的默认值


获取上传、下载的进度

上传和下载的异步处理事件是不同的:



  • 上传触发的是xhr.upload对象的 onprogress事件

  • 下载触发的是xhr对象的onprogress事件


事件的触发条件








































事件触发条件
onreadystatechange每当 xhr.readyState 改变时触发;但xhr.readyState由非0值变为0时不触发。
onloadstart调用xhr.send()方法后立即触发,若xhr.send()未被调用则不会触发此事件。
onprogressxhr.upload.onprogress在上传阶段(即xhr.send()之后,xhr.readystate=2之前)触发,每50ms触发一次;xhr.onprogress在下载阶段(即xhr.readystate=3时)触发,每50ms触发一次。
onload当请求成功完成时触发,此时xhr.readystate=4
onloadend当请求结束(包括请求成功和请求失败)时触发
onabort当调用xhr.abort()后触发
ontimeoutxhr.timeout不等于0,由请求开始即onloadstart开始算起,当到达xhr.timeout所设置时间请求还未结束即onloadend,则触发此事件。
onerror在请求过程中,若发生Network error则会触发此事件(若发生Network error时,上传还没有结束,则会先触发xhr.upload.onerror,再触发xhr.onerror;若发生Network error时,上传已经结束,则只会触发xhr.onerror)。注意,只有发生了网络层级别的异常才会触发此事件,对于应用层级别的异常,如响应返回的xhr.statusCode是4xx时,并不属于Network error,所以不会触发onerror事件,而是会触发onload事件。

事件触发顺序

当请求一切正常时,相关的事件触发顺序如下:



  1. 触发xhr.onreadystatechange(之后每次readyState变化时,都会触发一次)

  2. 触发xhr.onloadstart
    //上传阶段开始:

  3. 触发xhr.upload.onloadstart

  4. 触发xhr.upload.onprogress

  5. 触发xhr.upload.onload

  6. 触发xhr.upload.onloadend
    //上传结束,下载阶段开始:

  7. 触发xhr.onprogress

  8. 触发xhr.onload

  9. 触发xhr.onloadend

发生abort/timeout/error异常的处理

在请求的过程中,有可能发生 abort/timeout/error这3种异常。那么一旦发生这些异常,xhr后续会进行哪些处理呢?后续处理如下:



  1. 一旦发生abort或timeout或error异常,先立即中止当前请求

  2. 将 readystate 置为4,并触发 xhr.onreadystatechange事件

  3. 如果上传阶段还没有结束,则依次触发以下事件:

    1. xhr.upload.onprogress

    2. xhr.upload.[onabort或ontimeout或onerror]

    3. xhr.upload.onloadend


  4. 触发 xhr.onprogress事件

  5. 触发 xhr.[onabort或ontimeout或onerror]事件

  6. 触发xhr.onloadend 事件


结合Promise封装Ajax

既然学习了这么多的ajax相关知识,那么是时候学习致用了,我结合之前学过的Promise,封装一个简单的异步请求工具类:

首先我希望这个ajax模块具有以下功能



  • 将API资源集中管理,项目中使用api.js来管理整个项目或业务模块的api接口

  • 在定义api资源时,可以传递配置参数 http(‘xx.json‘, { options })

  • 各个业务调用api.js,获取相关接口,根据业务去决定调用方法(GET/POST/PUT等)api.news.get({})

  • 只在业务模块中使用的时候传递请求参数

  • 传递配置对象,可以独立使用 http({data:{}, method:‘Post‘, url:‘xx.json‘})









// api.js 集中管理api接口

var http = require(./http)

 

var api = {

news: http(‘api/news‘,{

cache: false //所有news下的接口都不缓存

}),

users: http(‘api/users‘)

}

 

//业务模块调用api.js

var api = require(‘api.js‘)

 

api.news.get({uname:‘xxx‘, pwd:123})

.then(res=>console.log(res))

.catch(err=>console.log(err))

 

api.news.post({uname:‘xxx‘, pwd:123})

.then(res=>console.log(res))

.catch(err=>console.log(err))

 

// 类似于jQuery的使用方式

http({

url:‘api/d1.json‘,

method: ‘post‘,//不区分大小写

data: {name:‘jack‘,pwd:123}

...

}).then().catch()

实现

先定义一个HttpAjax类,有一个构造函数constructor ,三个方法 inittoSearchParamssetHeaders









class HttpAjax(){

constructor(){}//1.初始化配置参数 2.判断是注册api资源还是直接调用

init(){} //返回各种Method对应的方法

toSearchParams(){} //将传递的json对象转为urlParams

setHeaders(){} //将配置的请求头对象设置请求头中去

}

代码很简单,这里就不详细解释了,这个封装仅用于学习,切勿用于生产环境









class HttpAjax{

constructor(opts, params){

const optIsString = typeof opts === ‘string‘;

this.methods = [‘GET‘, ‘POST‘];

const _opts = (optIsString ? params : opts) || {};

 

this.cache = typeof _opts.cache === undefined ? true : _opts.cache;

this.method = (_opts.method || ‘get‘).toLowerCase();

this.url = optIsString ? opts : _opts.url;

this.data = _opts.data || {};

this.headers = _opts.headers || {};

this.timeout = _opts.timeout || 6000;

this.respOnseType= _opts.responseType || ‘json‘;

this.requestBefore = _opts.requestBefore;

this.requestAfter = _opts.requestAfter;

this.requestAbort = _opts.requestAbort;

this.requestProgress = _opts.requestProgress;

this.Ontimeout= _opts.requestTimeout;

 

this.init();

 

return optIsString ? this : this[this.method](this.data);

}

init(){

this.methods.forEach(method=>{

this[method.toLowerCase()]= data => {

return new Promise((resolve, reject) => {

var xhr = new XMLHttpRequest();

!this.cache && method ===‘GET‘ && (this.url += ((/\?/).test(this.url) ? "&" : "?") + (new Date()).getTime());

xhr.open(method, this.url);

 

this.requestBefore && (xhr.Onloadstart= xhr => {

if(this.requestBefore(xhr)===false){

xhr.abort();

}

})

xhr.Onload= () => resolve(xhr.response);

xhr.Onerror= () => reject(xhr);

this.requestAbort && ( xhr.Onabort= this.requestAbort );

this.requestAfter && ( xhr.Onloadend= this.requestAfter );

this.requestTimeout && ( xhr.Ontimeout= this.requestTimeout );

this.requestProgress && ( xhr.Onprogress= this.requestProgress );

 

xhr.timeout = this.timeout;

xhr.respOnseType= this.responseType;

method===‘POST‘ && !this.headers && xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

this.headers && this.setHeaders(xhr, this.headers);

 

try{

xhr.send(this.toSearchParams(data));

}catch(e){

reject(err);

}

})

}

})

}

toSearchParams(jsonObj){

var params = [];

for(var i in jsonObj){

params.push(`${i}=${jsonObj[i]}`);

}

return params.join(‘&‘);

}

setHeaders(xhr, headers){

for(var i in headers){

xhr.setRequestHeader(i, headers[i]);

}

}

}

function http(options, params){

if(!options) return;

return new HttpAjax(options, params);

}

 

module.exports.http = http;

 

应用
点击请求一张图片,返回的结果:









http({

method:‘POST‘,

url:imgUrl,

cache:true,

responseType: ‘blob‘,

requestBefore: function(e){

console.log(‘准备开始请求...‘)

},

requestAfter:function(e){

console.log(‘请求完成‘)

},

requestProgress:function(e){

e.total>0 && console.log(‘正在接收:‘+ Math.ceil(e.loaded/e.total*100));

}

}).then(res=>{

document.getElementById(‘imgBox‘).src = URL.createObjectURL(res);

}).catch(err=>{

debugger;

console.log(err);

})

输出:

准备开始请求...

正在接收:2

正在接收:46

正在接收:100

请求完成

 

技术分享

下一篇将继续我们的Fetch API 探索…








推荐阅读
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文讨论了如何在codeigniter中识别来自angularjs的请求,并提供了两种方法的代码示例。作者尝试了$this->input->is_ajax_request()和自定义函数is_ajax(),但都没有成功。最后,作者展示了一个ajax请求的示例代码。 ... [详细]
  • 网络请求模块选择——axios框架的基本使用和封装
    本文介绍了选择网络请求模块axios的原因,以及axios框架的基本使用和封装方法。包括发送并发请求的演示,全局配置的设置,创建axios实例的方法,拦截器的使用,以及如何封装和请求响应劫持等内容。 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 从零基础到精通的前台学习路线
    随着互联网的发展,前台开发工程师成为市场上非常抢手的人才。本文介绍了从零基础到精通前台开发的学习路线,包括学习HTML、CSS、JavaScript等基础知识和常用工具的使用。通过循序渐进的学习,可以掌握前台开发的基本技能,并有能力找到一份月薪8000以上的工作。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文介绍了如何使用jQuery和AJAX来实现动态更新两个div的方法。通过调用PHP文件并返回JSON字符串,可以将不同的文本分别插入到两个div中,从而实现页面的动态更新。 ... [详细]
author-avatar
也碎羽落
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有