详谈ajax(原理、构造、封装......)

说起ajax,大家都不陌生。但是由于目前很多框架或者库等都对网络请求做了封装,导致了很多初学者只知其然而不知其所以然。所以今天我们就详细了解一下ajax的实现原理和封装ajax的关键步骤。

ajax的核心是XMLHttpRequest对象。首先我们先创建一个XMLHTTPRequest对象var xhr = new XMLHttpRequest();

注意:本文所提及的内容不兼容古老的IE,有想了解的同学自行查阅ActiveXObject相关内容。


XMLHttpRequest

在使用XMLHttpRequest对象的第一步,我们首先要调用open方法来初始化请求参数,xhr.open('get','/test',true),虽然名字叫open,但是此时请求还并没有发送。

open(method, url[, async, username, password])

  • method:请求类型,例如GET,POST等
  • url:请求地址(这里有同源限制,就是我们经常会看到的跨域问题啦)
  • async:是否发送异步请求。可选参数,默认为true。
  • username&password:可选参数,授权验证使用的,但是我们一般不这么用,使用后请求变成这个样子了,http(s)://username:password@url。

如果调用了open方法后再次对它进行调用,则相当于调用了abort方法,abort方法我们在后面介绍。

如果我们想为为请求绑定一些操作,这个时候就可以开始啦。常用的操作有如下几个:

setRequestHeader(key, value)

顾名思义,这个方法用于设置请求头内容。

  • key:要设置的请求头名称
  • value:对应请求头的值

overrideMimeType(type)

重写服务器返回的MIME类型。通过这个方法可以告诉服务器你想要的数据类型。

注意:以上这些操作必须定义在send方法之前。否则,就拿setRequestHeader来说,你都把请求发出去了再设置还有什么用?

这个时候,我们就可以通过调用send 方法来发送请求了,xhr.send(null)

send(data)

发送请求,如果是同步请求的话,会阻塞代码的执行,直至收到服务器响应才会继续。

  • data:发送给服务器的数据。为了兼容不同的浏览器,即使是不需要传数据,也需要传入参数null。

readyStateChanhe()

每次readyState的值改变的时候都会触发这个函数。

getResponseHeader(name)

获取指定响应头部的值,参数是响应头部的名称,并且不区分大小写。

getAllResponseHeaders()

获取服务器发送的所有HTTP响应的头部。

在这里我们穿插几个概念,readyState,这个属性表明了请求的状态,伴随HTTP请求的整个生命周期,它的值表明此时请求所处的阶段,具体如下:

readyState

数值 描述
0 初始化,open()尚未调用
1 open()已经调用,但是send未调用
2 已获取到返回头信息
3 正在下载返回体信息
4 请求完成

还有几个较为常用的属性

名称 含义
responseText 响应的文本
status 响应的状态码
statusText 响应的状态信息
responseXML 响应内容是“text/xml”或者是“application/xml”格式的时候,这个属性的值就是响应数据的XMLDOM文档。

我们用下面这段代码做个测试

var xhr = new XMLHttpRequest();
console.log(xhr.readyState)
xhr.onreadystatechange = function(){
    console.log('------')
    console.log('readyState:' + xhr.readyState)
    console.log('ResponseHeaders:' + xhr.getAllResponseHeaders())
    console.log('ResponseText:' + xhr.responseText.length)
    console.log('------')
}
xhr.open('get','/')
xhr.send(null)

下图我们可以直观的看到在创建了XMLHttpRequest对象的时候,readyState的值为0。

1.webp.jpg

然后我们定义了onreadystatechange函数,让其打印一些属性,并调用open方法,此时readyState变为1。

2.webp.jpg

最后我们调用send方法,可以看到经历了如下过程:

  1. send方法调用之后,readyState变为2,此时responseHeader已经获取到了,responseText为空;
  2. 响应数据开始下载,readyState变为3
  3. 响应数据下载结束,readyState变为4.我们可以发现此时responseText的长度比之前长。
3.webp.jpg

abort()

取消响应,调用这个方法会终止已发送的请求。我们尝试在之前的代码最后加一句。

xhr.abort();
console.log(xhr.readyState);

4.webp.jpg
5.webp.jpg

也就是说,send执行以后,并没有去尝试请求数据,而是直接取消掉了,并且我们发现abort会将readyState的值置为0。

除此之外,XMLHttpRequest还有一个很重要的属性withCredentials,cookie在同域请求的时候,会被自动携带在请求头中,但是跨域请求则不会,除非把withCredentials的值设为true(默认为false)。同时需要在服务端的响应头部中设置Access-Control-Allow-Credentials:true。不仅如此Access-Control-Allow-Origin的值也必须为当前页面的域名。


封装

到此为止,我们终于讲完了XMLHttpRequest的一些常用概念。接下来,我们尝试自己封装一个支持get和post的简易jax请求。

function ajax(url, option){
    option = option || {};
    var method = (option.method || 'GET').toUpperCase(),
        async = option.async === undefined ? true : option.async,
        params = handleParams(option.data);
    var xhr = new XMLHttpRequest();
    if(async){
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4){
               callback(option,xhr);
            }
        };
    }
    if (method === 'GET'){
        xhr.open("GET",url + '?' + params, async);
        xhr.send(null)
    }else if (method === 'POST'){
        xhr.open('POST', url, async);
        xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
        xhr.send(params);
    }
    if(!async){
        callback(option,xhr);
    }
    function callback(opt,obj){
        var status = obj.status;
        if (status >= 200 && status < 300 ){
            opt.success && opt.success(obj.responseText,obj.responseXML);
        }else{
            opt.fail && opt.fail(status);
        }
    }
    function handleParams(data) {  
        var arr = [];
        for (var i in data) {
            arr.push(encodeURIComponent(i) + '=' + encodeURIComponent(data[i]));
        }
        return arr.join('&');
    }
}
//  测试调用
ajax('/xxx',{
    method:'POST',
    data:{
        key: 'test'
    },
    success:function(){
        console.log('success')
    },
    fail:function(){
        console.log('fail')
    }
});

小结

其实ajax实现原理并不复杂,复杂的是容错、兼容性等的处理,使用的时候尽量使用库或者框架提供的封装,避免不必要的漏洞。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容

  •   2005 年,Jesse James Garrett 发表了一篇在线文章,题为“Ajax: A new App...
    霜天晓阅读 881评论 0 1
  • 本文详细介绍了 XMLHttpRequest 相关知识,涉及内容: AJAX、XMLHTTP、XMLHttpReq...
    semlinker阅读 13,594评论 2 18
  • 写在前面 本篇主要内容是结合“XMLHttpRequest Level 1”规范和w3school网站中针对XML...
    JSON_NULL阅读 1,395评论 3 9
  • 浏览器与服务器之间,采用HTTP协议通信。用户在浏览器地址栏键入一个网址,或者通过网页表单向服务器提交内容,这时浏...
    许先生__阅读 487评论 1 2
  • AJAX 原生js操作ajax 1.创建XMLHttpRequest对象 var xhr = new XMLHtt...
    碧玉含香阅读 3,146评论 0 7