前端跨域通訊的幾種方式

本文最初發表於博客園,並在GitHub上持續更新前端的系列文章。歡迎在GitHub上關注我,一塊兒入門和進階前端。javascript

如下是正文。php

前言

前端通訊類的問題,主要包括如下內容:html

  • 一、什麼是同源策略及限制

同源策略是一個概念,就一句話。有什麼限制,就三句話。能說出來便可。前端

  • 二、先後端如何通訊

若是你不許備,估計也就只能說出ajax。vue

  • 三、如何建立Ajax

Ajax在先後端通訊中常常用到。作業務時,能夠藉助第三方的庫,好比vue框架裏的庫、jQuery也有封裝好的方法。但若是讓你用原生的js去實現,該怎麼作?java

這就是考察你的動手能力,以及框架原理的掌握。若是能寫出來,能夠體現出你的基本功。git

  • 四、跨域通訊的幾種方式

這部分很是重要。無非就是問你:什麼是跨域、跨域有什麼限制、跨域有幾種方式github

下面分別講解。web

同源策略的概念和具體限制

同源策略:限制從一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的關鍵的安全機制。(來自MDN官方的解釋)面試

具體解釋:

(1)包括三個部分:協議、域名、端口(http協議的默認端口是80)。若是有任何一個部分不一樣,則不一樣,那就是跨域了。

(2)限制:這個源的文檔沒有權利去操做另外一個源的文檔。這個限制體如今:(要記住)

  • Cookie、LocalStorage和IndexDB沒法獲取。

  • 沒法獲取和操做DOM。

  • 不能發送Ajax請求。咱們要注意,Ajax只適合同源的通訊。

先後端如何通訊

主要有如下幾種方式:

  • Ajax:不支持跨域。

  • WebSocket:不受同源策略的限制,支持跨域。

  • CORS:不受同源策略的限制,支持跨域。一種新的通訊協議標準。能夠理解成是:同時支持同源和跨域的Ajax

如何建立Ajax

關於Ajax請求,能夠看本人的基礎文章:Ajax入門和發送http請求

在回答 Ajax 的問題時,要回答如下幾個方面:

  • 一、XMLHttpRequest 的工做原理

  • 二、兼容性處理

XMLHttpRequest只有在高級瀏覽器中才支持。在回答問題時,這個兼容性問題不要忽略。

  • 三、事件的出發條件

  • 四、事件的觸發順序

XMLHttpRequest有不少觸發事件,每一個事件是怎麼觸發的。

發送 Ajax 請求的五個步驟(XMLHttpRequest的工做原理)

(1)建立XMLHttpRequest 對象。

(2)使用open方法設置請求的參數。open(method, url, 是否異步)。

(3)發送請求。

(4)註冊事件。 註冊onreadystatechange事件,狀態改變時就會調用。

若是要在數據完整請求回來的時候才調用,咱們須要手動寫一些判斷的邏輯。

(5)獲取返回的數據,更新UI。

發送 get 請求和 post 請求

get請求舉例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<h1>Ajax 發送 get 請求</h1>
<input type="button" value="發送get_ajax請求" id='btnAjax'>

<script type="text/javascript">
    // 綁定點擊事件
    document.querySelector('#btnAjax').onclick = function () {
        // 發送ajax 請求 須要 五步

        // (1)建立異步對象
        var ajaxObj = new XMLHttpRequest();

        // (2)設置請求的參數。包括:請求的方法、請求的url。
        ajaxObj.open('get', '02-ajax.php');

        // (3)發送請求
        ajaxObj.send();

        //(4)註冊事件。 onreadystatechange事件,狀態改變時就會調用。
        //若是要在數據完整請求回來的時候才調用,咱們須要手動寫一些判斷的邏輯。
        ajaxObj.onreadystatechange = function () {
            // 爲了保證 數據 完整返回,咱們通常會判斷 兩個值
            if (ajaxObj.readyState == 4 && ajaxObj.status == 200) {
                // 若是可以進到這個判斷 說明 數據 完美的回來了,而且請求的頁面是存在的
                // 5.在註冊的事件中 獲取 返回的 內容 並修改頁面的顯示
                console.log('數據返回成功');

                // 數據是保存在 異步對象的 屬性中
                console.log(ajaxObj.responseText);

                // 修改頁面的顯示
                document.querySelector('h1').innerHTML = ajaxObj.responseText;
            }
        }
    }
</script>
</body>
</html>

post 請求舉例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
<h1>Ajax 發送 get 請求</h1>
<input type="button" value="發送put_ajax請求" id='btnAjax'>
<script type="text/javascript">

    // 異步對象
    var xhr = new XMLHttpRequest();

    // 設置屬性
    xhr.open('post', '02.post.php');

    // 若是想要使用post提交數據,必須添加此行
    xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    // 將數據經過send方法傳遞
    xhr.send('name=fox&age=18');

    // 發送並接受返回值
    xhr.onreadystatechange = function () {
        // 這步爲判斷服務器是否正確響應
        if (xhr.readyState == 4 && xhr.status == 200) {
            alert(xhr.responseText);
        }
    };
</script>
</body>
</html>

onreadystatechange 事件

註冊 onreadystatechange 事件後,每當 readyState 屬性改變時,就會調用 onreadystatechange 函數。

readyState:(存有 XMLHttpRequest 的狀態。從 0 到 4 發生變化)

  • 0: 請求未初始化

  • 1: 服務器鏈接已創建

  • 2: 請求已接收

  • 3: 請求處理中

  • 4: 請求已完成,且響應已就緒

事件的觸發條件

事件的觸發順序

上圖的參考連接:

實際開發中用的 原生Ajax請求

var util = {};

    //獲取 ajax 請求以後的json
    util.json = function (options) {

        var opt = {
            url: '',
            type: 'get',
            data: {},
            success: function () {
            },
            error: function () {
            },

        };
        util.extend(opt, options);
        if (opt.url) {
            //IE兼容性處理:瀏覽器特徵檢查。檢查該瀏覽器是否存在XMLHttpRequest這個api,沒有的話,就用IE的api
            var 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提交數據,必須添加此行
            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);  //將字符串轉成json
                        opt.success.call(xhr, res);
                    }
                }
            } else {
                if (opt.error && opt.error instanceof Function) {
                    opt.error.call(xhr, res);
                }
            }
        };
    }

Ajax 的推薦連接:http://www.javashuo.com/article/p-kiecxjqk-eu.html

跨域通訊的幾種方式

方式以下:

  • 一、JSONP

  • 二、WebSocket

  • 三、CORS

  • 四、Hash

  • 五、postMessage

上面這五種方式,在面試時,都要說出來。

一、JSONP

面試會問:JSONP的原理是什麼?怎麼實現的?

在CORS和postMessage之前,咱們一直都是經過JSONP來作跨域通訊的。

JSONP的原理:經過<script>標籤的異步加載來實現的。好比說,實際開發中,咱們發現,head標籤裏,能夠經過<script>標籤的src,裏面放url,加載不少在線的插件。這就是用到了JSONP。

JSONP的實現:

好比說,客戶端這樣寫:

<script src="http://www.smyhvae.com/?data=name&callback=myjsonp"></script>

上面的src中,data=name是get請求的參數,myjsonp是和後臺約定好的函數名。
服務器端這樣寫:

myjsonp({
            data: {}

        })

因而,本地要求建立一個myjsonp 的全局函數,才能將返回的數據執行出來。

實際開發中,前端的JSONP是這樣實現的:

<script>

    var util = {};

    //定義方法:動態建立 script 標籤
    /**
     * [function 在頁面中注入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'); //事先約定好的 函數名
        window[callbackName] = function () {      //根據回調名稱註冊一個全局的函數
            if (onsuccess && util.isFunction(onsuccess)) {
                onsuccess(arguments[0]);
            }
        };
        var script = util.createScript(url + '&callback=' + callbackName, charset);   //動態建立一個script標籤
        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); //往html中增長這個標籤,目的是把請求發送出去
    };

</script>

二、WebSocket

WebSocket的用法以下:

//

    var ws = new WebSocket('wss://echo.websocket.org'); //建立WebSocket的對象。參數能夠是 ws 或 wss,後者表示加密。

    //把請求發出去
    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.');
    };

Websocket的推薦連接:http://www.ruanyifeng.com/blog/2017/05/websocket.html

三、CORS

CORS 能夠理解成是既能夠同步、也能夠異步*的Ajax。

fetch 是一個比較新的API,用來實現CORS通訊。用法以下:

// url(必選),options(可選)
      fetch('/some/url/', {
          method: 'get',
      }).then(function (response) {  //相似於 ES6中的promise

      }).catch(function (err) {
        // 出錯了,等價於 then 的第二個參數,但這樣更好用更直觀
      });

推薦連接裏有詳細的配置。

另外,若是面試官問:「CORS爲何支持跨域的通訊?」

答案:跨域時,瀏覽器會攔截Ajax請求,並在http頭中加Origin。

四、Hash

url的#後面的內容就叫Hash。Hash的改變,頁面不會刷新。這就是用 Hash 作跨域通訊的基本原理。

補充:url的?後面的內容叫Search。Search的改變,會致使頁面刷新,所以不能作跨域通訊。

使用舉例:

場景:個人頁面 A 經過iframe或frame嵌入了跨域的頁面 B。

如今,我這個A頁面想給B頁面發消息,怎麼操做呢?

(1)首先,在個人A頁面中:

//僞代碼
    var B = document.getElementsByTagName('iframe');
    B.src = B.src + '#' + 'jsonString';  //咱們能夠把JS 對象,經過 JSON.stringify()方法轉成 json字符串,發給 B

(2)而後,在B頁面中:

// B中的僞代碼
    window.onhashchange = function () {  //經過onhashchange方法監聽,url中的 hash 是否發生變化
        var data = window.location.hash;
    };

五、postMessage()方法

H5中新增的postMessage()方法,能夠用來作跨域通訊。既然是H5中新增的,那就必定要提到。

場景:窗口 A (http:A.com)向跨域的窗口 B (http:B.com)發送信息。步驟以下。

(1)在A窗口中操做以下:向B窗口發送數據:

// 窗口A(http:A.com)向跨域的窗口B(http:B.com)發送信息
    Bwindow.postMessage('data', 'http://B.com'); //這裏強調的是B窗口裏的window對象

(2)在B窗口中操做以下:

// 在窗口B中監聽 message 事件
    Awindow.addEventListener('message', function (event) {   //這裏強調的是A窗口裏的window對象
        console.log(event.origin);  //獲取 :url。這裏指:http://A.com
        console.log(event.source);  //獲取:A window對象
        console.log(event.data);    //獲取傳過來的數據
    }, false);

個人公衆號

想學習代碼以外的軟技能?不妨關注個人微信公衆號:生命團隊(id:vitateam)。

掃一掃,你將發現另外一個全新的世界,而這將是一場美麗的意外:

相關文章
相關標籤/搜索