一、什么是同源策略及限制
- 同源策略限制從一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用于隔離潛在惡意文件的關鍵的安全機制。
- 源:協議、域名和端口, 默認端口是80
三者有一個不同,即源不同,就是跨域
https://www.baidu.com:80/
https://www.baidu.com:8080
// 不同源
- 限制:不是一個源的文檔,你沒有權利去操作另一個源的文檔,主要限制方面
3-1. Cookie、LocalStorage 和 IndexDB 無法讀取
3-2. DOM無法獲得
3-3. Ajax 請求不能發送
二、前后端如何通信
- Ajax
同源下通信方式 - WebSocket
支持同源和跨域通信 - CORS
支持同源和跨域通信
三、如何創建Ajax
- XMLHttpRequest 對象的工作流程
1-1 聲明對象,兼容IE
1-2 根據請求方式的不同,設置不同傳參方式
1-3 open方法,確定發送的方式、地址、是否異步
1-4 send方法,發送請求
1-5 監聽響應狀態變化, 處理返回數據
/*** [json 實現ajax的json]* @param {[type]} options [description]* @return {[type]} [description]*/util.json = function (options) {var opt = {url: '',type: 'get',data: {},success: function () {},error: function () {},};util.extend(opt, options);if (opt.url) {// ①聲明對象,兼容IEvar xhr = XMLHttpRequest? new XMLHttpRequest(): new ActiveXObject('Microsoft.XMLHTTP');var data = opt.data,url = opt.url,type = opt.type.toUpperCase(),dataArr = [];for (var k in data) {dataArr.push(k + '=' + data[k]);}// ② 根據請求方式的不同,傳遞參數的方式也不相同if (type === 'GET') {url = url + '?' + dataArr.join('&');// ③ open方法,確定發送的方式、地址、是否異步xhr.open(type, url.replace(/\?$/g, ''), true);// ④ send方法,發送請求xhr.send();}if (type === 'POST') {xhr.open(type, url, true);xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');xhr.send(dataArr.join('&'));}// ⑤ 監聽響應狀態變化xhr.onload = function () {// ⑥ 處理返回數據, 200表示成功返回 304表示緩存中讀取, 如果請求是媒體資源,還需要判斷206,因為媒體資源是分段返回的if (xhr.status === 200 || xhr.status === 304) {var res;if (opt.success && opt.success instanceof Function) {res = xhr.responseText;if (typeof res === 'string') {res = JSON.parse(res);opt.success.call(xhr, res);}}} else {if (opt.error && opt.error instanceof Function) {opt.error.call(xhr, res);}}};}};
- 兼容性處理
- 事件的觸發條件
- 事件的觸發順序
四、跨域通信的幾種方式
1.JSONP
- JSONP: 再出來postMessage之前,一直使用JSONP進行跨域通信
- JSONP 原理: 利用script標簽的異步加載來實現的
- JSONP 具體的實現邏輯
3-1 向服務端發送請求,并告訴服務端callback的名稱
<script src="http:www.abc.com/?callback=jsonp"></script>
3-2 服務端返回數據,全局注冊jsonp函數,同時執行本地jsonp函數
<script src="http:www.abc.com/?callback=jsonp"></script>
<script>jsonp({data: {}})
</script>
-
代碼實現步驟
4-1 確定回調函數的名稱
4-2 根據名稱注冊全局函數
4-3 動態創建scrpit標簽
4-4 監聽腳本的加載事件
4-5 監聽加載是否成功,成功后,刪除該script標簽,同時刪除全局函數
4-6 往html里面新增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');window[callbackName] = function () {if (onsuccess && util.isFunction(onsuccess)) {onsuccess(arguments[0]);}};var script = util.createScript(url + '&callback=' + callbackName, charset);script.onload = script.onreadystatechange = function () {if (!script.readyState || /loaded|complete/.test(script.readyState)) {script.onload = script.onreadystatechange = null;// 移除該script的 DOM 對象if (script.parentNode) {script.parentNode.removeChild(script);}// 刪除函數或變量window[callbackName] = null;}};script.onerror = function () {if (onerror && util.isFunction(onerror)) {onerror();}};document.getElementsByTagName('head')[0].appendChild(script);};
2.Hash
- Hash: url中,#號后面的東西為hash, hash的變動,頁面不會刷新
- search: url中,?號后面的東西叫search,search的改變,頁面會刷新,所以search不能做跨域通信
- 使用hash通信 代碼實現
// 利用hash,場景是當前頁面 A 通過iframe或frame嵌入了跨域的頁面 B// 在A中偽代碼如下:var B = document.getElementsByTagName('iframe');B.src = B.src + '#' + 'data';// 在B中的偽代碼如下window.onhashchange = function () {var data = window.location.hash;};
3.postMessage
- postMessage:同源策略是限制跨域通信,實際業務中又需要跨域通信,使用postMessage實現跨域通信,H5新出的
- postMessage代碼實現
// postMessage// 窗口A(http:A.com)向跨域的窗口B(http:B.com)發送信息Awindow.postMessage('data', 'http://B.com');// 在窗口B中監聽Bwindow.addEventListener('message', function (event) {console.log(event.origin); // 確定發送的源, http://A.comconsole.log(event.source); // A window的引用console.log(event.data); // data}, false);
4.WebSocket
- WebSocket:支持跨域通信
- WebSocket 代碼實現
// Websocket【參考資料】http://www.ruanyifeng.com/blog/2017/05/websocket.html// wss 服務器地址var ws = new WebSocket('wss://echo.websocket.org');// 發送請求ws.onopen = function (evt) {console.log('Connection open ...');ws.send('Hello WebSockets!');};// 接收消息ws.onmessage = function (evt) {console.log('Received Message: ', evt.data);ws.close();};// 關閉連接ws.onclose = function (evt) {console.log('Connection closed.');};
5.CORS
- CORS:支持跨域通信的Ajax
// CORS【參考資料】http://www.ruanyifeng.com/blog/2016/04/cors.html// url(必選),options(可選)fetch('/some/url/', {method: 'get',}).then(function (response) {}).catch(function (err) {// 出錯了,等價于 then 的第二個參數,但這樣更好用更直觀});