摘要:所以今天我們就詳細了解一下的實現原理和封裝的關鍵步驟。接下來,我們嘗試自己封裝一個支持和的簡易請求。測試調用小結其實實現原理并不復雜,復雜的是容錯兼容性等的處理,使用的時候盡量使用庫或者框架提供的封裝,避免不必要的漏洞。
說起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。
然后我們定義了onreadystatechange函數,讓其打印一些屬性,并調用open方法,此時readyState變為1。
最后我們調用send方法,可以看到經歷了如下過程:
send方法調用之后,readyState變為2,此時responseHeader已經獲取到了,responseText為空;
響應數據開始下載,readyState變為3
響應數據下載結束,readyState變為4.我們可以發現此時responseText的長度比之前長。
abort()
取消響應,調用這個方法會終止已發送的請求。我們嘗試在之前的代碼最后加一句。
xhr.abort(); console.log(xhr.readyState);
也就是說,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實現原理并不復雜,復雜的是容錯、兼容性等的處理,使用的時候盡量使用庫或者框架提供的封裝,避免不必要的漏洞。
補充感謝@蝦嗶嗶的提問,這里做個簡單的補充說明。
async是一個可選的布爾值參數,默認為true,意味著是否執行異步操作,如果值為false,則send()方法不會返回任何東西,直到接受到了服務器的返回數據。如果為值為true,一個對開發者透明的通知會發送到相關的事件監聽者。這個值必須是true,如果multipart 屬性是true,否則將會出現一個意外。
根據我的分析,當async為false的時候,readyState會在send方法之后直接由1變成4。也就是說異步模式,send方法會立刻返回。同步模式下,只有響應完全接受后,send才會返回。
另外,由于同步模式會阻塞,較新版本的Chrome在主線程上的同步請求已被棄用。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/95877.html
摘要:開公眾號差不多兩年了,有不少原創教程,當原創越來越多時,大家搜索起來就很不方便,因此做了一個索引幫助大家快速找到需要的文章系列處理登錄請求前后端分離一使用完美處理權限問題前后端分離二使用完美處理權限問題前后端分離三中密碼加鹽與中異常統一處理 開公眾號差不多兩年了,有不少原創教程,當原創越來越多時,大家搜索起來就很不方便,因此做了一個索引幫助大家快速找到需要的文章! Spring Boo...
摘要:大家中秋假期快樂,假期分享一些原理設計文章給大家原創不易,歡迎轉發,一起學習凌晨寫的,不容易哈,收藏或者點個贊吧在常見的單頁應用中,我們都會有一個根文件,里面放置一個然后配置路由來切換很多人在子父組件嵌套關系下的生命周期鉤子函數如何應用, 大家中秋假期快樂,假期分享一些原理設計文章給大家 原創不易,歡迎轉發,一起學習(凌晨寫的,不容易哈,收藏或者點個贊吧) 在常見的單頁應用中,我們都...
摘要:下面我會詳細地從源碼的角度分析下文簡寫成是如何實現自動注入的原理。文件解析器,解析對應的文件信息,并將文件信息注冊到中。節點解析器,用于構建節點信息。注冊與綁定類,將的類信息與綁定。 微信公眾號「后端進階」,專注后端技術分享:Java、Golang、WEB框架、分布式中間件、服務治理等等。 老司機傾囊相授,帶你一路進階,來不及解釋了快上車! mybatis-plus是完全基于myba...
閱讀 3093·2021-11-22 09:34
閱讀 593·2021-11-22 09:34
閱讀 2437·2021-10-08 10:18
閱讀 3372·2021-09-22 15:57
閱讀 2585·2021-09-22 15:25
閱讀 2398·2019-08-30 15:54
閱讀 2093·2019-08-30 15:44
閱讀 1800·2019-08-29 11:18