硬核知識點——瀏覽器中的三類五種請求

{"type":"doc","content":[{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關注公衆號“執鳶者”,回覆“"},{"type":"text","marks":[{"type":"strong"}],"text":"紅寶書"},{"type":"text","text":"”獲取“"},{"type":"text","marks":[{"type":"strong"}],"text":"javaScript高級程序第四版(pdf)"},{"type":"text","text":"”及大量前端學習資料。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對瀏覽器的請求進行劃分,可以分爲三類:一般請求、Ajax請求、WebSocket請求,對於每種請求都有不同的產生方式,今天就以這個思想爲主線來一起嘮一嘮。"}]}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e9/e9bb54ef6c9b98e42fb20bd24efc47af.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"一、一般請求"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此處說的一般請求就是指瀏覽器會直接顯示響應體數據,這些請求會刷新\\跳轉頁面。換個更加容易理解的說法吧,指的就是控制檯Network面板中除了XHR和WS部分顯示的請求。例如js、css、img資源。"}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"二、Ajax請求"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Ajax請求也是由瀏覽器發出,但是不會對界面進行任何操作,只是調用監視的回調函數並傳入響應相關數據,發出Ajax請求可以通過三種方式:XHR、Fetch、Axios,其餘的均不是Ajax請求。"}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1 XHR"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最早將Ajax推到歷史舞臺的關鍵技術就是XMLHttpRequest(XHR)對象,雖然目前已經有了一些過時的嫌疑,但是還是很有必要提一下它。下面就按照一個請求的整個生命週期來看一看該技術。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一、 對象的實例化"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然要使用XHR,第一步就是要將該對象實例化"}]}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"const xhr = new XMLHttpRequest();"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"二、初始化操作"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將對象實例化後是不是緊接着就需要進行初始化操作,到底該請求要發給誰、通過什麼請求發、該請求到底是同步發還是異步發"}]}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"xhr.open(method, url, async)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"三、請求頭設置"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"瞭解網絡的同學本肯定知道請求頭的概念,既然要與後端打交道,請求頭還是有必要進行設置的(默認的配置不一定滿足我們高大上的需求),例如想發送json格式的內容,這個時候就需要設置Content-Type爲application/json"}]}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"xhr.setRequestHeader('Content-Type', 'application/json');"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"四、接收請求的準備工作"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"瀏覽器除了設置常見的請求頭外,還需要指定響應數據類型,得到響應後好自動解析。目前支持的類型有string、arraybuffer、blob、document、json、text、ms-stream。"}]}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"xhr.responseType('json')"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"五、發送請求"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前期工作都準備好了,接下來就是激動人心的時刻了,看好呀,要按開始鍵發送請求啦。"}]}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"xhr.send(data)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"六、監聽響應"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我喊一聲美女,人家肯定要回應一下呀,畢竟顏值在這,不迴應該是多麼不給面子的一件事呀!!!爲了等待人家的迴應,則需要分三步進行:"}]}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"進入監聽狀態,放在這就是通過onreadystatechange進行監聽。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"等待正面迴應。readyStatus表徵目前的狀態,當readyStatus爲4(請求完成),響應算是接收到了"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"處理響應。不能一股腦的處理全部響應吧,畢竟也是要面子的人,我肯定只希望接收我喜歡的信息吧,就喜歡狀態碼在200~299之間的,別的一概pass掉。"}]}]}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"xhr.onreadystatechange = () => {\n if (xhr.readyState == 4) {\n if (xhr.status >= 200 && xhr.status < 300) {\n console.log(xhr.response);\n }\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"七、中斷請求"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">正常流程算是走完了,肯定還有非正常流程,發起請求後我後悔了,不想得到對方的迴應了,此時仍然後辦法——中斷請求"}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"xhr.abort()"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注:本文不是文檔學習,詳細使用請見https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest"}]}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.2 Fetch"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"長江後浪推前浪,互聯網技術發展這麼快,出現了新的技術(Fetch)能夠執行XMLHttpRequest對象的所有任務,該技術使用更容易,接口更現代化,能夠在Web工作線程等現代Web工具中使用。(Fetch必須是異步,XMLHttpRequest可同步可異步)。"}]}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"const payload = JSON.stringify({\n test: 'test'\n});\n\nlet headersObj = new Headers({\n 'Content-Type':'application/json'\n});\n\nlet request = new Request('http://localhost:8080');\n\nfetch(request, {\n method: 'POST',\n body: payload,\n headers: headersObj\n})\n.then((response) => response.json())\n.then(console.log)"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述代碼雖然簡單,但是已經囊括了Fetch API中所有的概念:fetch、Headers、Request、Response、Body混入。"}]}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"fetch()"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"fetch()方法暴露在全局作用域中,包括主頁面執行線程、模塊和工作線程,調用該方法,瀏覽器就會向給定URL發送請求。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)fetch(input[, init]):接收兩個參數,input爲要獲取的資源,__init爲一個配置對象,配置需要傳入的參數,滿足更多複雜的需求"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)返回一個promise對象,從而鏈式的進行處理"}]},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"Headers"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"相當於 response/request 的頭信息,可以使你查詢到這些頭信息,或者針對不同的結果做不同的操作。該對象包含檢索、設置、添加、刪除,設置完自己需要的頭信息後就可以將其掛載到fetch中的配置信息中。"}]}]},{"type":"numberedlist","attrs":{"start":"3","normalizeStart":"3"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"Request"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該對象是獲取資源請求的接口,暴露了請求和相關信息。可以將該對象的實例作爲fetch函數中的第一個參數"}]}]},{"type":"numberedlist","attrs":{"start":"4","normalizeStart":"4"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"Response"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該對象是獲取資源響應的接口,並暴露了響應的相關信息。"}]}]},{"type":"numberedlist","attrs":{"start":"5","normalizeStart":"5"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"Body混入"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"提供了與 response/request 中的 body 有關的方法,可以定義它的內容形式以及處理方式。在Body混入中提供了5個方法,用於將ReadableStream轉存到緩衝區的內存中,將緩衝區轉換爲某種JavaScript對象類型,以及通過Promise產生結果。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(1)Body.text():返回Promise,解決將緩衝區轉存得到的UTF-8格式字符串"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(2)Body.json():返回Promise,解決將緩衝區轉存得到的JSON"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(3)Body.formData():返回Promise,解決將緩衝區轉存得到的FormData實例"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(4)Body.arrayBuffer():返回Promise,解決將緩衝區轉存得到的ArrayBuffer"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(5)Body.text():返回Promise,解決將緩衝區轉存得到的Blob實例"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.3 Axios"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Axios應該是目前前端最流行的Ajax請求庫,具有以下特點:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1. 基於Promise的異步Ajax請求庫"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2. 瀏覽器端/node端都可以使用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3. 支持請求/響應攔截器"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4. 支持請求取消"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5. 請求/響應數據轉換"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"6. 批量發送請求"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於Axios還是比較有意思的,本次只說一下其簡單使用,"},{"type":"text","marks":[{"type":"strong"}],"text":"下一期準備剖析一下其源碼,有興趣的小夥伴可以先搬好小板凳佔個坑,關注一下。"}]}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"// 默認配置\naxios.defaults.baseURL = 'http://localhost:8080'\n\n// 請求攔截器\naxios.interceptors.request.use(\n config => {\n console.log('request interceptor resolved');\n return config;\n },\n error => {\n console.log('request interceptor rejected');\n return Promise.reject(error);\n }\n);\n\n// 響應攔截器\naxios.interceptors.response.use(\n response => {\n console.log('response interceptor resolved');\n return response;\n },\n error => {\n console.log('response interceptor rejected');\n return Promise.reject(error);\n }\n);\n\nlet cancel; // 用於保存取消請求的函數\naxios('/', {\n method: 'post',\n headers: {\n 'Content-Type': 'application/json'\n },\n data: {\n test: 'test'\n },\n // 取消請求\n cancelToken: new axios.CancelToken((c) => {\n cancel = c;\n })\n})\n.then((response) => {\n console.log(response.data)\n})\n\n// 若想取消請求,直接調用下面函數\n// cancel();"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述代碼已經囊括了Axios庫中大多數核心內容,包括axios()函數、默認設置、請求/響應攔截器、取消請求(內部設計的很巧妙,想知道的請看下期講解)"}]}]},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"axios()"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"完成相應配置併發送請求,調用方式有多種語法糖,同學們可以按需使用。"}]}]},{"type":"numberedlist","attrs":{"start":"2","normalizeStart":"2"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"默認設置"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過axios.defaults.xxx可以完成很多全局配置,提高代碼的複用。(提高複用真是完美的編碼思想)"}]}]},{"type":"numberedlist","attrs":{"start":"3","normalizeStart":"3"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"請求/響應攔截器"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請求攔截器的作用就是在請求發送之前先進行一些列的處理;響應攔截器的作用就是觸發請求的回調之前執行響應攔截器,對響應做一些預處理操作"}]}]},{"type":"numberedlist","attrs":{"start":"4","normalizeStart":"4"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"取消請求"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過配置cancelToken對象並緩存用於取消請求的cancel函數,在需要的時候觸發該函數取消請求(內部其實就是調用的xhr.abort())"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於更多使用見詳細使用文檔https://github.com/axios/axios"}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"三、WebSocket請求"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面來聊聊這個傳奇協議——WebSocket,WebSockt通過一個長時連接實現與服務器全雙工、雙向的通信。(特別提醒:同源策略不適用於WebSocket)"}]}]},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"let ws = new WebSocket('ws://127.0.0.1:8080');\n\n// 在連接建立成功時\nws.onopen = () => {\n ws.send('websocket')\n}\n\n// 在接收到消息時\nws.onmessage = (event) => {\n console.log(event.data);\n}\n\n// 在發生錯誤時\nws.onerror = () => {\n console.log('error');\n}\n\n// 在連接關閉時\nws.onclose = () => {\n console.log('close');\n}"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上述代碼已經囊括大部分WebSocket的概念,實例化WebSocket建立與服務端的連接;通過事件監聽即可瞭解WebSokcet連接目前的狀態;通過send()函數即可向服務端發送內容;當服務端發送消息時即可觸發message事件,通過event.data屬性獲取其有效載荷。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">本篇文章雖然比較簡單,但是可以幫助我們認清楚請求其實是分爲三類的,這是我最最最大的收穫,歡迎小夥伴們能夠給出自己的想法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"1.如果覺得這篇文章還不錯,來個分享、點贊吧,讓更多的人也看到"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"2.關注公衆號執鳶者,領取學習資料,定期爲你推送原創深度好文"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/49/493fa4b32454a5732898c5c972e803b5.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章