前端百題斬—瀏覽器中的請求們
通過瀏覽器的控制臺可以看到訪問一個頁面的時候會發起多個請求,并且這一系列請求會被分為多個類別,那么這些類別除了類型不同之外,還有什么區別呢?
通過查找資料和自己的嘗試,可將瀏覽器的請求進行劃分為三類:一般請求、Ajax請求、WebSocket請求,對于每種請求都有不同的產生方式。
1 一般請求
此處說的一般請求就是指瀏覽器會直接顯示響應體數據,這些請求會刷新\跳轉頁面。換個更加容易理解的說法吧,指的就是控制臺Network面板中除了XHR和WS部分顯示的請求。例如js、css、img資源。
2 Ajax請求
Ajax請求也是由瀏覽器發出,但是不會對界面進行任何操作,只是調用監視的回調函數并傳入響應相關數據,發出Ajax請求可以通過三種方式:XHR、Fetch、Axios,其余的均不是Ajax請求。
2.1 XHR
最早將Ajax推到歷史舞臺的關鍵技術就是XMLHttpRequest(XHR)對象,雖然目前已經有了一些過時的嫌疑,但是還是很有必要提一下它。下面就按照一個請求的整個生命周期來看一看該技術。
一、 對象的實例化
既然要使用XHR,第一步就是要將該對象實例化
- const xhr = new XMLHttpRequest();
二、初始化操作
將對象實例化后是不是緊接著就需要進行初始化操作,到底該請求要發給誰、通過什么請求發、該請求到底是同步發還是異步發
- xhr.open(method, url, async)
三、請求頭設置
了解網絡的同學本肯定知道請求頭的概念,既然要與后端打交道,請求頭還是有必要進行設置的(默認的配置不一定滿足我們高大上的需求),例如想發送json格式的內容,這個時候就需要設置Content-Type為application/json
- xhr.setRequestHeader('Content-Type', 'application/json');
四、接收請求的準備工作
瀏覽器除了設置常見的請求頭外,還需要指定響應數據類型,得到響應后好自動解析。目前支持的類型有string、arraybuffer、blob、document、json、text、ms-stream。
- xhr.responseType('json')
五、發送請求
前期工作都準備好了,接下來就是激動人心的時刻了,看好呀,要按開始鍵發送請求啦。
- xhr.send(data)
六、監聽響應
我喊一聲美女,人家肯定要回應一下呀,畢竟顏值在這,不回應該是多么不給面子的一件事呀!!!為了等待人家的回應,則需要分三步進行:
- 進入監聽狀態,放在這就是通過onreadystatechange進行監聽。
- 等待正面回應。readyStatus表征目前的狀態,當readyStatus為4(請求完成),響應算是接收到了
- 處理響應。不能一股腦的處理全部響應吧,畢竟也是要面子的人,我肯定只希望接收我喜歡的信息吧,就喜歡狀態碼在200~299之間的,別的一概pass掉。
- xhr.onreadystatechange = () => {
- if (xhr.readyState == 4) {
- if (xhr.status >= 200 && xhr.status < 300) {
- console.log(xhr.response);
- }
- }
- }
七、中斷請求
正常流程算是走完了,肯定還有非正常流程,發起請求后我后悔了,不想得到對方的回應了,此時仍然后辦法——中斷請求
- xhr.abort()
注:本文不是文檔學習,詳細使用請見https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest
2.2 Fetch
長江后浪推前浪,互聯網技術發展這么快,出現了新的技術(Fetch)能夠執行XMLHttpRequest對象的所有任務,該技術使用更容易,接口更現代化,能夠在Web工作線程等現代Web工具中使用。(Fetch必須是異步,XMLHttpRequest可同步可異步)。
- const payload = JSON.stringify({
- test: 'test'
- });
- let headersObj = new Headers({
- 'Content-Type':'application/json'
- });
- let request = new Request('http://localhost:8080');
- fetch(request, {
- method: 'POST',
- body: payload,
- headers: headersObj
- })
- .then((response) => response.json())
- .then(console.log)
上述代碼雖然簡單,但是已經囊括了Fetch API中所有的概念:fetch、Headers、Request、Response、Body混入。
fetch()
- fetch()方法暴露在全局作用域中,包括主頁面執行線程、模塊和工作線程,調用該方法,瀏覽器就會向給定URL發送請求。(1)fetch(input[, init]):接收兩個參數,input為要獲取的資源,__init為一個配置對象,配置需要傳入的參數,滿足更多復雜的需求 (2)返回一個promise對象,從而鏈式的進行處理
Headers
- 相當于 response/request 的頭信息,可以使你查詢到這些頭信息,或者針對不同的結果做不同的操作。該對象包含檢索、設置、添加、刪除,設置完自己需要的頭信息后就可以將其掛載到fetch中的配置信息中。
Request
- 該對象是獲取資源請求的接口,暴露了請求和相關信息。可以將該對象的實例作為fetch函數中的第一個參數
Response
- 該對象是獲取資源響應的接口,并暴露了響應的相關信息。
Body混入
- 提供了與 response/request 中的 body 有關的方法,可以定義它的內容形式以及處理方式。在Body混入中提供了5個方法,用于將ReadableStream轉存到緩沖區的內存中,將緩沖區轉換為某種JavaScript對象類型,以及通過Promise產生結果。
(1)Body.text():返回Promise,解決將緩沖區轉存得到的UTF-8格式字符串
(2)Body.json():返回Promise,解決將緩沖區轉存得到的JSON
(3)Body.formData():返回Promise,解決將緩沖區轉存得到的FormData實例
(4)Body.arrayBuffer():返回Promise,解決將緩沖區轉存得到的ArrayBuffer
(5)Body.text():返回Promise,解決將緩沖區轉存得到的Blob實例
2.3 Axios
Axios應該是目前前端最流行的Ajax請求庫,具有以下特點:
- 基于Promise的異步Ajax請求庫
- 瀏覽器端/node端都可以使用
- 支持請求/響應攔截器
- 支持請求取消
- 請求/響應數據轉換
- 批量發送請求
- // 默認配置
- axios.defaults.baseURL = 'http://localhost:8080'
- // 請求攔截器
- axios.interceptors.request.use(
- config => {
- console.log('request interceptor resolved');
- return config;
- },
- error => {
- console.log('request interceptor rejected');
- return Promise.reject(error);
- }
- );
- // 響應攔截器
- axios.interceptors.response.use(
- response => {
- console.log('response interceptor resolved');
- return response;
- },
- error => {
- console.log('response interceptor rejected');
- return Promise.reject(error);
- }
- );
- let cancel; // 用于保存取消請求的函數
- axios('/', {
- method: 'post',
- headers: {
- 'Content-Type': 'application/json'
- },
- data: {
- test: 'test'
- },
- // 取消請求
- cancelToken: new axios.CancelToken((c) => {
- cancel = c;
- })
- })
- .then((response) => {
- console.log(response.data)
- })
- // 若想取消請求,直接調用下面函數
- // cancel();
- 上述代碼已經囊括了Axios庫中大多數核心內容,包括axios()函數、默認設置、請求/響應攔截器、取消請求(內部設計的很巧妙,想知道的請看下期講解)
axios()
- 完成相應配置并發送請求,調用方式有多種語法糖,同學們可以按需使用。
默認設置
- 通過axios.defaults.xxx可以完成很多全局配置,提高代碼的復用。(提高復用真是完美的編碼思想)
請求/響應攔截器
- 請求攔截器的作用就是在請求發送之前先進行一些列的處理;響應攔截器的作用就是觸發請求的回調之前執行響應攔截器,對響應做一些預處理操作
取消請求
- 通過配置cancelToken對象并緩存用于取消請求的cancel函數,在需要的時候觸發該函數取消請求(內部其實就是調用的xhr.abort())
- 對于更多使用見詳細使用文檔https://github.com/axios/axios
3 WebSocket請求
下面來聊聊這個傳奇協議——WebSocket,WebSockt通過一個長時連接實現與服務器全雙工、雙向的通信。(特別提醒:同源策略不適用于WebSocket)
- let ws = new WebSocket('ws://127.0.0.1:8080');
- // 在連接建立成功時
- ws.onopen = () => {
- ws.send('websocket')
- }
- // 在接收到消息時
- ws.onmessage = (event) => {
- console.log(event.data);
- }
- // 在發生錯誤時
- ws.onerror = () => {
- console.log('error');
- }
- // 在連接關閉時
- ws.onclose = () => {
- console.log('close');
- }
- 上述代碼已經囊括大部分WebSocket的概念,實例化WebSocket建立與服務端的連接;通過事件監聽即可了解WebSokcet連接目前的狀態;通過send()函數即可向服務端發送內容;當服務端發送消息時即可觸發message事件,通過event.data屬性獲取其有效載荷。