前端跨域通信的几种方式
前言
前端通信類(lèi)的問(wèn)題,主要包括以下內(nèi)容:
- 1、什么是同源策略及限制
同源策略是一個(gè)概念,就一句話。有什么限制,就三句話。能說(shuō)出來(lái)即可。
- 2、前后端如何通信
如果你不準(zhǔn)備,估計(jì)也就只能說(shuō)出ajax。
- 3、如何創(chuàng)建Ajax
Ajax在前后端通信中經(jīng)常用到。做業(yè)務(wù)時(shí),可以借助第三方的庫(kù),比如vue框架里的庫(kù)、jQuery也有封裝好的方法。但如果讓你用原生的js去實(shí)現(xiàn),該怎么做?
這就是考察你的動(dòng)手能力,以及框架原理的掌握。如果能寫(xiě)出來(lái),可以體現(xiàn)出你的基本功。
- 4、跨域通信的幾種方式
這部分非常重要。無(wú)非就是問(wèn)你:什么是跨域、跨域有什么限制、跨域有幾種方式。
下面分別講解。
同源策略的概念和具體限制
同源策略:限制從一個(gè)源加載的文檔或腳本如何與來(lái)自另一個(gè)源的資源進(jìn)行交互。這是一個(gè)用于隔離潛在惡意文件的關(guān)鍵的安全機(jī)制。(來(lái)自MDN官方的解釋)
具體解釋:
(1)源包括三個(gè)部分:協(xié)議、域名、端口(http協(xié)議的默認(rèn)端口是80)。如果有任何一個(gè)部分不同,則源不同,那就是跨域了。
(2)限制:這個(gè)源的文檔沒(méi)有權(quán)利去操作另一個(gè)源的文檔。這個(gè)限制體現(xiàn)在:(要記住)
-
Cookie、LocalStorage和IndexDB無(wú)法獲取。
-
無(wú)法獲取和操作DOM。
-
不能發(fā)送Ajax請(qǐng)求。我們要注意,Ajax只適合同源的通信。
前后端如何通信
主要有以下幾種方式:
-
Ajax:不支持跨域。
-
WebSocket:不受同源策略的限制,支持跨域。
-
CORS:不受同源策略的限制,支持跨域。一種新的通信協(xié)議標(biāo)準(zhǔn)。可以理解成是:同時(shí)支持同源和跨域的Ajax。
如何創(chuàng)建Ajax
關(guān)于Ajax請(qǐng)求,可以看本人的基礎(chǔ)文章:Ajax入門(mén)和發(fā)送http請(qǐng)求
在回答 Ajax 的問(wèn)題時(shí),要回答以下幾個(gè)方面:
-
1、XMLHttpRequest 的工作原理
-
2、兼容性處理
XMLHttpRequest只有在高級(jí)瀏覽器中才支持。在回答問(wèn)題時(shí),這個(gè)兼容性問(wèn)題不要忽略。
-
3、事件的出發(fā)條件
-
4、事件的觸發(fā)順序
XMLHttpRequest有很多觸發(fā)事件,每個(gè)事件是怎么觸發(fā)的。
發(fā)送 Ajax 請(qǐng)求的五個(gè)步驟(XMLHttpRequest的工作原理)
(1)創(chuàng)建XMLHttpRequest 對(duì)象。
(2)使用open方法設(shè)置請(qǐng)求的參數(shù)。open(method, url, 是否異步)。
(3)發(fā)送請(qǐng)求。
(4)注冊(cè)事件。 注冊(cè)onreadystatechange事件,狀態(tài)改變時(shí)就會(huì)調(diào)用。
如果要在數(shù)據(jù)完整請(qǐng)求回來(lái)的時(shí)候才調(diào)用,我們需要手動(dòng)寫(xiě)一些判斷的邏輯。
(5)獲取返回的數(shù)據(jù),更新UI。
發(fā)送 get 請(qǐng)求和 post 請(qǐng)求
get請(qǐng)求舉例:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Document</title> </head> <body> <h1>Ajax 發(fā)送 get 請(qǐng)求</h1> <input type="button" value="發(fā)送get_ajax請(qǐng)求" id='btnAjax'><script type="text/javascript">// 綁定點(diǎn)擊事件document.querySelector('#btnAjax').onclick = function () {// 發(fā)送ajax 請(qǐng)求 需要 五步// (1)創(chuàng)建異步對(duì)象var ajaxObj = new XMLHttpRequest();// (2)設(shè)置請(qǐng)求的參數(shù)。包括:請(qǐng)求的方法、請(qǐng)求的url。ajaxObj.open('get', '02-ajax.php');// (3)發(fā)送請(qǐng)求ajaxObj.send();//(4)注冊(cè)事件。 onreadystatechange事件,狀態(tài)改變時(shí)就會(huì)調(diào)用。//如果要在數(shù)據(jù)完整請(qǐng)求回來(lái)的時(shí)候才調(diào)用,我們需要手動(dòng)寫(xiě)一些判斷的邏輯。ajaxObj.onreadystatechange = function () {// 為了保證 數(shù)據(jù) 完整返回,我們一般會(huì)判斷 兩個(gè)值if (ajaxObj.readyState == 4 && ajaxObj.status == 200) {// 如果能夠進(jìn)到這個(gè)判斷 說(shuō)明 數(shù)據(jù) 完美的回來(lái)了,并且請(qǐng)求的頁(yè)面是存在的// 5.在注冊(cè)的事件中 獲取 返回的 內(nèi)容 并修改頁(yè)面的顯示console.log('數(shù)據(jù)返回成功');// 數(shù)據(jù)是保存在 異步對(duì)象的 屬性中console.log(ajaxObj.responseText);// 修改頁(yè)面的顯示document.querySelector('h1').innerHTML = ajaxObj.responseText;}}} </script> </body> </html>post 請(qǐng)求舉例:
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Document</title> </head> <body> <h1>Ajax 發(fā)送 get 請(qǐng)求</h1> <input type="button" value="發(fā)送put_ajax請(qǐng)求" id='btnAjax'> <script type="text/javascript">// 異步對(duì)象var xhr = new XMLHttpRequest();// 設(shè)置屬性xhr.open('post', '02.post.php');// 如果想要使用post提交數(shù)據(jù),必須添加此行xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");// 將數(shù)據(jù)通過(guò)send方法傳遞xhr.send('name=fox&age=18');// 發(fā)送并接受返回值xhr.onreadystatechange = function () {// 這步為判斷服務(wù)器是否正確響應(yīng)if (xhr.readyState == 4 && xhr.status == 200) {alert(xhr.responseText);}}; </script> </body> </html>onreadystatechange 事件
注冊(cè) onreadystatechange 事件后,每當(dāng) readyState 屬性改變時(shí),就會(huì)調(diào)用 onreadystatechange 函數(shù)。
readyState:(存有 XMLHttpRequest 的狀態(tài)。從 0 到 4 發(fā)生變化)
-
0: 請(qǐng)求未初始化
-
1: 服務(wù)器連接已建立
-
2: 請(qǐng)求已接收
-
3: 請(qǐng)求處理中
-
4: 請(qǐng)求已完成,且響應(yīng)已就緒
事件的觸發(fā)條件
事件的觸發(fā)順序
上圖的參考鏈接:
- 你真的會(huì)使用XMLHttpRequest嗎?
實(shí)際開(kāi)發(fā)中用的 原生Ajax請(qǐng)求
var util = {};//獲取 ajax 請(qǐng)求之后的jsonutil.json = function (options) {var opt = {url: '',type: 'get',data: {},success: function () {},error: function () {},};util.extend(opt, options);if (opt.url) {//IE兼容性處理:瀏覽器特征檢查。檢查該瀏覽器是否存在XMLHttpRequest這個(gè)api,沒(méi)有的話,就用IE的apivar xhr = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP');var data = opt.data,url = opt.url,type = opt.type.toUpperCase();dataArr = [];}for (var key in data) {dataArr.push(key + '=' + data[key]);}if (type === 'GET') {url = url + '?' + dataArr.join('&');xhr.open(type, url.replace(/\?$/g, ''), true);xhr.send();}if (type === 'POST') {xhr.open(type, url, true);// 如果想要使用post提交數(shù)據(jù),必須添加此行xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");xhr.send(dataArr.join('&'));}xhr.onload = function () {if (xhr.status === 200 || xhr.status === 304) { //304表示:用緩存即可。206表示獲取媒體資源的前面一部分var res;if (opt.success && opt.success instanceof Function) {res = xhr.responseText;if (typeof res === 'string') {res = JSON.parse(res); //將字符串轉(zhuǎn)成jsonopt.success.call(xhr, res);}}} else {if (opt.error && opt.error instanceof Function) {opt.error.call(xhr, res);}}};}Ajax 的推薦鏈接:https://segmentfault.com/a/1190000006669043
跨域通信的幾種方式
方式如下:
-
1、JSONP
-
2、WebSocket
-
3、CORS
-
4、Hash
-
5、postMessage
上面這五種方式,在面試時(shí),都要說(shuō)出來(lái)。
1、JSONP
面試會(huì)問(wèn):JSONP的原理是什么?怎么實(shí)現(xiàn)的?
在CORS和postMessage以前,我們一直都是通過(guò)JSONP來(lái)做跨域通信的。
JSONP的原理:通過(guò)<script>標(biāo)簽的異步加載來(lái)實(shí)現(xiàn)的。比如說(shuō),實(shí)際開(kāi)發(fā)中,我們發(fā)現(xiàn),head標(biāo)簽里,可以通過(guò)<script>標(biāo)簽的src,里面放url,加載很多在線的插件。這就是用到了JSONP。
JSONP的實(shí)現(xiàn):
比如說(shuō),客戶端這樣寫(xiě):
<script src="http://www.smyhvae.com/?data=name&callback=myjsonp"></script>上面的src中,data=name是get請(qǐng)求的參數(shù),myjsonp是和后臺(tái)約定好的函數(shù)名。
服務(wù)器端這樣寫(xiě):
于是,本地要求創(chuàng)建一個(gè)myjsonp 的全局函數(shù),才能將返回的數(shù)據(jù)執(zhí)行出來(lái)。
實(shí)際開(kāi)發(fā)中,前端的JSONP是這樣實(shí)現(xiàn)的:
<script>var util = {};//定義方法:動(dòng)態(tài)創(chuàng)建 script 標(biāo)簽/*** [function 在頁(yè)面中注入js腳本]* @param {[type]} url [description]* @param {[type]} charset [description]* @return {[type]} [description]*/util.createScript = function (url, charset) {var script = document.createElement('script');script.setAttribute('type', 'text/javascript');charset && script.setAttribute('charset', charset);script.setAttribute('src', url);script.async = true;return script;};/*** [function 處理jsonp]* @param {[type]} url [description]* @param {[type]} onsucess [description]* @param {[type]} onerror [description]* @param {[type]} charset [description]* @return {[type]} [description]*/util.jsonp = function (url, onsuccess, onerror, charset) {var callbackName = util.getName('tt_player'); //事先約定好的 函數(shù)名window[callbackName] = function () { //根據(jù)回調(diào)名稱(chēng)注冊(cè)一個(gè)全局的函數(shù)if (onsuccess && util.isFunction(onsuccess)) {onsuccess(arguments[0]);}};var script = util.createScript(url + '&callback=' + callbackName, charset); //動(dòng)態(tài)創(chuàng)建一個(gè)script標(biāo)簽script.onload = script.onreadystatechange = function () { //監(jiān)聽(tīng)加載成功的事件,獲取數(shù)據(jù)if (!script.readyState || /loaded|complete/.test(script.readyState)) {script.onload = script.onreadystatechange = null;// 移除該script的 DOM 對(duì)象if (script.parentNode) {script.parentNode.removeChild(script);}// 刪除函數(shù)或變量window[callbackName] = null; //最后不要忘了刪除}};script.onerror = function () {if (onerror && util.isFunction(onerror)) {onerror();}};document.getElementsByTagName('head')[0].appendChild(script); //往html中增加這個(gè)標(biāo)簽,目的是把請(qǐng)求發(fā)送出去};</script>2、WebSocket
WebSocket的用法如下:
//var ws = new WebSocket('wss://echo.websocket.org'); //創(chuàng)建WebSocket的對(duì)象。參數(shù)可以是 ws 或 wss,后者表示加密。//把請(qǐng)求發(fā)出去ws.onopen = function (evt) {console.log('Connection open ...');ws.send('Hello WebSockets!');};//對(duì)方發(fā)消息過(guò)來(lái)時(shí),我接收ws.onmessage = function (evt) {console.log('Received Message: ', evt.data);ws.close();};//關(guān)閉連接ws.onclose = function (evt) {console.log('Connection closed.');};Websocket的推薦鏈接:http://www.ruanyifeng.com/blog/2017/05/websocket.html
3、CORS
CORS 可以理解成是既可以同步、也可以異步*的Ajax。
fetch 是一個(gè)比較新的API,用來(lái)實(shí)現(xiàn)CORS通信。用法如下:
// url(必選),options(可選)fetch('/some/url/', {method: 'get',}).then(function (response) { //類(lèi)似于 ES6中的promise}).catch(function (err) {// 出錯(cuò)了,等價(jià)于 then 的第二個(gè)參數(shù),但這樣更好用更直觀});- CORS的推薦鏈接:http://www.ruanyifeng.com/blog/2016/04/cors.html
推薦鏈接里有詳細(xì)的配置。
另外,如果面試官問(wèn):“CORS為什么支持跨域的通信?”
答案:跨域時(shí),瀏覽器會(huì)攔截Ajax請(qǐng)求,并在http頭中加Origin。
4、Hash
url的#后面的內(nèi)容就叫Hash。Hash的改變,頁(yè)面不會(huì)刷新。這就是用 Hash 做跨域通信的基本原理。
補(bǔ)充:url的?后面的內(nèi)容叫Search。Search的改變,會(huì)導(dǎo)致頁(yè)面刷新,因此不能做跨域通信。
使用舉例:
場(chǎng)景:我的頁(yè)面 A 通過(guò)iframe或frame嵌入了跨域的頁(yè)面 B。
現(xiàn)在,我這個(gè)A頁(yè)面想給B頁(yè)面發(fā)消息,怎么操作呢?
(1)首先,在我的A頁(yè)面中:
//偽代碼var B = document.getElementsByTagName('iframe');B.src = B.src + '#' + 'jsonString'; //我們可以把JS 對(duì)象,通過(guò) JSON.stringify()方法轉(zhuǎn)成 json字符串,發(fā)給 B(2)然后,在B頁(yè)面中:
// B中的偽代碼window.onhashchange = function () { //通過(guò)onhashchange方法監(jiān)聽(tīng),url中的 hash 是否發(fā)生變化var data = window.location.hash;};5、postMessage()方法
H5中新增的postMessage()方法,可以用來(lái)做跨域通信。既然是H5中新增的,那就一定要提到。
場(chǎng)景:窗口 A (http:A.com)向跨域的窗口 B (http:B.com)發(fā)送信息。步驟如下。
(1)在A窗口中操作如下:向B窗口發(fā)送數(shù)據(jù):
// 窗口A(http:A.com)向跨域的窗口B(http:B.com)發(fā)送信息Bwindow.postMessage('data', 'http://B.com'); //這里強(qiáng)調(diào)的是B窗口里的window對(duì)象(2)在B窗口中操作如下:
// 在窗口B中監(jiān)聽(tīng) message 事件Awindow.addEventListener('message', function (event) { //這里強(qiáng)調(diào)的是A窗口里的window對(duì)象console.log(event.origin); //獲取 :url。這里指:http://A.comconsole.log(event.source); //獲取:A window對(duì)象console.log(event.data); //獲取傳過(guò)來(lái)的數(shù)據(jù)}, false);?
總結(jié)
以上是生活随笔為你收集整理的前端跨域通信的几种方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 简单c语言程序例子与运行结果图,C语言程
- 下一篇: 2017年html5行业报告,云适配发布