前言
學妹這學期新開了一門課《Script及AJAX開發技術》,然而臨近學期末,她突然跑來問我:到底什么是AJAX ?相信很多人(尤其是前端)在寫代碼的時候經常會用到AJAX技術,但是如果真要說出個所以然,可能還會有些困難。其實簡單概括下,AJAX就是一種利用 JAVAScript 向服務端發起請求,并獲得服務端響應的技術。它的特點是異步請求,局部刷新。
Tips:這里我將技術二字加粗了,是因為很多初學者會以為AJAX是一個庫/框架,類似于JQuery/Vue之類的,因而有很多初學者會提出該怎么安裝AJAX的問題。事實上AJAX是一種技術。
雖然概括起來很簡單,但是AJAX技術的一些細節仍然值得我們思考,接下來我會詳細的介紹。
AJAX 解決的問題
我們剛才說過了,AJAX是一種發送請求的技術,那在AJAX被發明前,瀏覽器是如何請求的呢?
- 地址欄。用戶在地址欄輸入 http://baidu.com ,按回車,就向 http://baidu.com 發起了一個請求。(同時頁面刷新)
- a 標簽。用戶點擊頁面中的 a 鏈接,也會發起一個請求。(同時頁面刷新)
- img 標簽。頁面中如果有 img 標簽,那么就會發起一個對此圖片的請求(頁面沒有刷新,但是只能請求圖片)類似的還有 link 標簽、script 標簽,都可以對一類文件的請求。
在這三種方式中,除了第三種,其他兩種方式想要發送一個請求,就必須要刷新頁面,如果頁面只有展示內容的話刷新一下自然無所謂,但倘若一個頁面有很多的表單內容需要填寫,而你在最后填寫完成提交的時候才告訴你,其中某一個地方不符合要求,要你回去重填,然后刷新一下頁面,內容都消失了,怕是當時就可能會氣的暴走了吧。
也正是這種極端的用戶體驗讓微軟創新地開發了一個接口 ActiveXObject("Microsoft.XMLHTTP"),并在 IE 5.0 中開放給開發者用。通過該接口,瀏覽器可以向服務器發送請求并取回所需的數據,并在客戶端采用 JavaScript 處理來自服務器的回應。這就是 AJAX 的前身。隨后這種技術被谷歌的開發人員發現并運用在 Gmail 中,再然后就是 W3C 制定了一個標準用來規范 AJAX,至此 AJAX 算是正式成為每一個前端開發者的必備技能了。
通過 AJAX 技術,服務器和瀏覽器之間交換的數據大量減少,服務器回應更快了。同時,很多的處理工作可以在發出請求的客戶端機器上完成,因此服務端的負荷也減少了許多。
AJAX 的原理
那 AJAX 的實現原理又是什么呢?我們先來看一下AJAX的定義,以下內容摘自維基百科:
AJAX即“Asynchronous JavaScript and XML”(異步的JavaScript與XML技術),指的是一套綜合了多項技術的瀏覽器端網頁開發技術。
這里又出現了一個新的名詞:異步。這個名詞在計算機領域可以說是一個很重要的名詞了,很多技術都離不開異步二字,比如Nodejs的異步非阻塞I/O模型,當然這就是題外話了。我們應該怎么理解這里的異步呢?
不急不急,我們先來看一個生活中非常常見的例子:
這種場景在上學的時候很常見,其實AJAX的原理和上述流程很相似,不信你看下面:
在上述例子中,核心是班長(也就是HXR對象),班主任可以通過他傳遞消息(客戶端構建XHR對象發送請求)然后收到響應。在班長去通知小明的過程中,班主任仍然可以繼續手頭的工作,這就是一個異步的過程。(果然生活處處皆學問)
那么我們又該如何在代碼中使用這個XHR對象呢?
AJAX 的使用
XHR 的全稱是 XMLHttpRequest,這是由微軟首先引入的一個特性,其他瀏覽器提供商后來都提供了相同的實現。這跟以前的技術最大的不同點在于「頁面無需刷新」,僅此而已。
想要使用 AJAX 發起一個請求很簡單,一共 4 步。
1. 創建一個 XHR 對象(需要考慮瀏覽器差異)
2.監聽請求成功后的狀態變化
第三行的 request.responseText 就是服務器返回的內容了(默認是字符串)
3.設置請求參數
request.open(method,url,async);
請求的三個參數分別是:請求的方法、請求的地址、和是否采用異步請求。
4.發送請求
request.send();
說實話,雖然只有4步,但是通過這種原生的方法發送請求還是覺得有些復雜,那有沒有什么簡單的方法呢?
AJAX 的其他使用方式
JQuery 使用AJAX
JQuery將上述過程封裝的很好,使用起來也非常簡單(只舉出最簡單的例子,詳細還請移步官方文檔):
Vue 使用 AJAX
vue官方推薦使用axIOS來進行請求,這里同樣舉出一個最簡單的例子
微信小程序使用AJAX
微信小程序的請求就是wx.request這個api,wx.request(一些對象參數),微信小程序不同于瀏覽器的ajax請求,可以直接跨域請求不用考慮跨域問題。
拓展
HTTP狀態碼
看到這其實我們就可以發送數據了,那怎么接收返回的數據呢?事實上,這已經不是在AJAX的討論范圍了,但是作為一個拓展知識點,我還是想介紹下狀態碼這個東西。狀態碼的作用是服務器返回給客戶端的用來描述HTTP請求的狀態的。用來描述HTTP請求的狀態碼太多了,這里介紹一些常見的狀態碼。
- 200 表示從請求成功
- 301 表示永久性重定向。該狀態碼表示請求的資源已被分配了新的URI,以后應使用資源現在所指的URI。
- 302 表示臨時性重定向。
- 404 表示服務器上找不到請求的資源。
- 500 表示服務器端在執行請求時發生了錯誤。多半是因為Web應用存在的bug或某些臨時的故障。
- 503 表示服務器暫時處于超負載或正在進行停機維護,現在無法處理請求。
獲取網頁中的XHR請求
這時就有人可能會問了,有沒有什么辦法可以獲取一個網頁中的XHR請求呢?當然是有的,這一過程其實說的寬泛點其實就是抓包,這里我以掘金為例,介紹下獲取網頁中的XHR請求。
首先我們打開Chrome瀏覽器,然后進入開發者工具(按F12或者網頁右擊選擇“檢查”),選擇Network選項卡,我們可以發現下面有很多東西,比如Filter、All、HXR、JS等等,通過這個工具這里我們可以看見一個網頁渲染過程中的所有請求(不只是XHR,還有JS、css等)。
隨后我們選擇XHR,就會出現請求這個網頁過程中的所有的XHR請求了。包括name、status、size等信息。
之前提到過了,我們通過XHR攜帶的數據返回給瀏覽器渲染頁面,到底是怎么實現的呢?不急,我們先來看一下現在的頁面是什么樣的:
其實這些東西都在其中一個XHR中,于是我們隨便點擊一個名為query的XHR對象(其實并不是隨便點擊的),然后移到Response選項卡:
我們看到了很長的一段JSON數據,格式化后(這里我們可以直接切換到Preview選項卡)篩選出一部分可以看到:
是不是剛才那個頁面里面的東西都在這里面呢?
簡單分析下
既然都獲取到請求數據了,再不分析下都感覺對不起這么多的數據了,讓我們把選項卡從Response移到Headers上,我們驚訝的發現竟然出現了好多東西:
這里我們簡單說明下各個參數:
General 部分
首先是General部分:
我們可以看出,請求地址是 https://web-api.juejin.im/query,請求方法為POST方法,請求狀態是200,也就是請求成功了,同時還可以知道這次請求的IP地址是 119.254.97.159:443。
Referrer Policy
這里說下Referrer Policy這個字段,這個字段解釋起來有點小麻煩,我們知道當用戶在瀏覽器上點擊一個鏈接時,會產生一個 HTTP 請求,用于獲取新的頁面內容,而在該請求的報頭中,會包含一個 Referrer,用以指定該請求是從哪個頁面跳轉頁來的,常被用于分析用戶來源等信息。但是也有成為用戶的一個不安全因素,比如有些網站直接將 sessionid 或是 token 放在地址欄里傳遞的,會原樣不動地當作 Referrer 報頭的內容傳遞給第三方網站。所以就有了 Referrer Policy,用于過濾 Referrer 報頭內容,目前是一個候選標準,不過已經有部分瀏覽器支持該標準。這里為 no-referrer-when-downgrade的意思是指當發生降級(比如從 https:// 跳轉到 http:// )時,不傳遞 Referrer 報頭。但是反過來的話不受影響。通常也會當作瀏覽器的默認安全策略。
Headers 部分
接下來是Response Headers和Request Headers,這里說實話我覺得沒什么好說的,稍微有些重要的就是請求體Content-Type,為什么說他重要呢?我們往下看。
接下來一段是Request Payload,Form Data我們比較熟悉,那這個Request Payload又是個什么東西呢?我們知道前端開發中經常會用到AJAX發送異步請求,對于POST類型的請求會附帶請求數據。而常用的傳參方式有兩種,其一是Form Data,另一個就是Request Payload了。
那這兩者有何區別呢?其實區別主要就是在Content-Type上,這也就是為啥我說他重要的原因。
Form Data 和 Request Payload 區別
- 如果請求頭里設置 Content-Type:Application/x-www-form-urlencoded,那么這個請求被認為是表單請求,參數出現在Form Data里,格式為key=value&key=value&key=value...
- 原生的AJAX請求頭里設置 Content-Type:application/json,或者使用默認的請求頭 Content-Type:text/plain參,數會顯示在Request payload塊里提交,參數格式為JSON格式: {"key":"value","key":"value"…},可讀性會更好。
Fetch API
既然XHR這么方便,是不是就沒有不足之處呢?當然不是。XHR 很實用,但并不是一個設計優良的 API,在設計上并不符合職責分離原則,輸入、輸出以及狀態都雜糅在同一對象中,并用事件機制來跟蹤狀態變化。并且,基于事件的模型與最近流行的 Promise 和 generator 異步編程模型不太友好。因此Fetch API橫空出世,它旨在修正上述缺陷,它提供了與 HTTP 語義相同的 JS 語法,簡單來說,它引入了 fetch()這個實用的方法來獲取網絡資源。
當然由于文章篇幅有限,這里僅僅只是引出Fetch API,推薦閱讀 http://bubkoo.com/2015/05/08/introduction-to-fetch/。
最后
其實剛開始只是想簡單介紹下 AJAX 的原理,但是后來發現用了班主任讓班長找小明這例子之后,AJAX 的原理似乎也就明白了,便想著要不就擴展點吧,以至于整篇文章有將近一半的篇幅在寫擴展的知識了。不過也不算喧賓奪主,畢竟也是 AJAX 衍生出的知識點。
如果你對本篇文章的內容有所疑問,可以在評論區寫下你的觀點;如果你覺得本篇文章對你有所幫助,希望可以掃描下方二維碼關注公眾號「01二進制」。您的支持是我前進的最大動力!
參考
- 「每日一題」AJAX 是什么?
- Ajax原理一篇就夠了
- HTTP請求中的form data和request payload的區別
- Form Data vs Request Payload
- 微信開放文檔
- fetch API 簡介
- Referrer Policy 介紹