1. 程式人生 > 其它 >c++ 外部元件發生異常_Vue元件化開發之插槽

c++ 外部元件發生異常_Vue元件化開發之插槽

技術標籤:計算機網路(HTTP)Node.js

跨域

1. JSONP:

只要說到跨域,就必須聊到 JSONP,JSONP全稱為:JSON with Padding,可用於解決主流瀏覽器的跨域資料訪問的問題。

Web 頁面上呼叫 js 檔案不受瀏覽器同源策略的影響,所以通過 Script 便籤可以進行跨域的請求:

  1. 首先前端先設定好回撥函式,並將其作為 url 的引數。
  2. 服務端接收到請求後,通過該引數獲得回撥函式名,並將資料放在引數中將其返回
  3. 收到結果後因為是 script 標籤,所以瀏覽器會當做是指令碼進行執行,從而達到跨域獲取資料的目的。

例項:

後端邏輯:

//server.js
const
url = require('url'); require('http').createServer((req, res) => { const data = { x: 10 }; const callback = url.parse(req.url, true).query.callback; res.writeHead(200); res.end(`${callback}(${JSON.stringify(data)})`); }).listen(3000, '127.0.0.1'); console.log('啟動服務,監聽 127.0.0.1:3000'
);

通過 node server.js 啟動服務,監聽埠 3000,這樣服務端就建立起來了

前端頁面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>index.html</title>
</head>
<body>
    <script>
	function jsonpCallback(data) {
	    alert('獲得 X 資料:'
+ data.x); }
</script> <script src="http://127.0.0.1:3000?callback=jsonpCallback"></script> </body> </html>

邏輯已經寫好了,那如何來模擬一個跨域的場景呢?

這裡我們通過埠號的不同來模擬跨域的場景,通過 http://127.0.0.1:8080 埠來訪問頁面。先通過 npm 下載 http-server 模組:

nom install -g http-server

並且在頁面同目錄下輸入:

http-server

img

這樣就可以通過埠 8080 訪問 index.html 剛才那個頁面了,相當於是開啟兩個監聽不同埠的 http 伺服器,通過頁面中的請求來模擬跨域的場景。開啟瀏覽器,訪問 http://127.0.0.1:8080 就可以看到從 http://127.0.0.1:3000 獲取到的資料了。

img

至此,通過 JSONP 跨域獲取資料已經成功了,但是通過這種事方式也存在著一定的優缺點:

優點:

  1. 它不像XMLHttpRequest 物件實現 Ajax 請求那樣受到同源策略的限制
  2. 相容性很好,在古老的瀏覽器也能很好的執行
  3. 不需要 XMLHttpRequest 或 ActiveX 的支援;並且在請求完畢後可以通過呼叫 callback 的方式回傳結果。

缺點:

  1. 它支援 GET 請求而不支援 POST 等其它類行的 HTTP 請求。
  2. 它只支援跨域 HTTP 請求這種情況,不能解決不同域的兩個頁面或 iframe 之間進行資料通訊的問題

2. CORS:

CORS 是一個 W3C 標準,全稱是"跨域資源共享"(Cross-origin resource sharing)它允許瀏覽器向跨源伺服器,發出 XMLHttpRequest 請求,從而克服了 ajax 只能同源使用的限制。

CORS 需要瀏覽器和伺服器同時支援才可以生效,對於開發者來說,CORS 通訊與同源的 ajax 通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現 ajax 請求跨源,就會自動新增一些附加的頭資訊,有時還會多出一次附加的請求,但使用者不會有感覺。

因此,實現 CORS 通訊的關鍵是伺服器。只要伺服器實現了 CORS 介面,就可以跨源通訊。

首先前端先建立一個 index.html 頁面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CORS</title>
</head>
<body>
    <script>
	const xhr = new XMLHttpRequest();
	xhr.open('GET', 'http://127.0.0.1:3000', true);
	xhr.onreadystatechange = function() {
	    if(xhr.readyState === 4 && xhr.status === 200) {
	        alert(xhr.responseText);
	    }
	}
	xhr.send(null);
    </script>
</body>
</html>

這似乎跟一次正常的非同步 ajax 請求沒有什麼區別,關鍵是在服務端收到請求後的處理:

require('http').createServer((req, res) => {

    res.writeHead(200, {
	'Access-Control-Allow-Origin': 'http://localhost:8080'
    });
    res.end('這是你要的資料:1111');

}).listen(3000, '127.0.0.1');

console.log('啟動服務,監聽 127.0.0.1:3000');

關鍵是在於設定相應頭中的 Access-Control-Allow-Origin,該值要與請求頭中 Origin 一致才能生效,否則將跨域失敗。

接下來再次開啟兩個 http 伺服器程序:

img

開啟瀏覽器訪問 http://localhost:8080 就可以看到:

img

成功的關鍵在於 Access-Control-Allow-Origin 是否包含請求頁面的域名,如果不包含的話,瀏覽器將認為這是一次失敗的非同步請求,將會呼叫 xhr.onerror 中的函式。

CORS 的優缺點:

  1. 使用簡單方便,更為安全
  2. 支援 POST 請求方式
  3. CORS 是一種新型的跨域問題的解決方案,存在相容問題,僅支援 IE 10 以上

這裡只是對 CORS 做一個簡單的介紹,如果想更詳細地瞭解其原理的話,可以看看下面這篇文章:

跨域資源共享 CORS 詳解 - 阮一峰的網路日誌

3. Server Proxy:

伺服器代理,顧名思義,當你需要有跨域的請求操作時傳送請求給後端,讓後端幫你代為請求,然後最後將獲取的結果傳送給你。

假設有這樣的一個場景,你的頁面需要獲取 CNode:Node.js專業中文社群 論壇上一些資料,如通過 https://cnodejs.org/api/v1/topics,當時因為不同域,所以你可以將請求後端,讓其對該請求代為轉發。

程式碼如下:

const url = require('url');
const http = require('http');
const https = require('https');

const server = http.createServer((req, res) => {
    const path = url.parse(req.url).path.slice(1);
    if(path === 'topics') {
	https.get('https://cnodejs.org/api/v1/topics', (resp) => {
	    let data = "";
	    resp.on('data', chunk => {
		data += chunk;
	    });
	    resp.on('end', () => {
		res.writeHead(200, {
		    'Content-Type': 'application/json; charset=utf-8'
		});
		res.end(data);
	    });
	})		
    }
}).listen(3000, '127.0.0.1');

console.log('啟動服務,監聽 127.0.0.1:3000');

通過程式碼你可以看出,當你訪問 http://127.0.0.1:3000 的時候,伺服器收到請求,會代你傳送請求 https://cnodejs.org/api/v1/topics 最後將獲取到的資料傳送給瀏覽器。

同樣地開啟服務:

img

開啟瀏覽器訪問 http://localhost:3000/topics,就可以看到

img

跨域請求成功。

純粹的跨全域請求的方式已經介紹完了,另外介紹四種通過 iframe 跨域與其它頁面通訊的方式。

4. location.hash:

在 url 中,http://www.baidu.com#helloworld 的 “#helloworld” 就是 location.hash,改變 hash 值不會導致頁面重新整理,所以可以利用 hash 值來進行資料的傳遞,當然資料量是有限的。

假設 localhost:8080 下有檔案 cs1.html 要和 localhost:8081 下的 cs2.html 傳遞訊息,cs1.html 首先建立一個隱藏的 iframe,iframe 的 src 指向 localhost:8081/cs2.html,這時的 hash 值就可以做引數傳遞。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CS1</title>
</head>
<body>
    <script>
	// http://localhost:8080/cs1.html
	let ifr = document.createElement('iframe');
	ifr.style.display = 'none';
	ifr.src = "http://localhost:8081/cs2.html#data";
	document.body.appendChild(ifr);
		
	function checkHash() {
	    try {
		let data = location.hash ? location.hash.substring(1) : '';
		console.log('獲得到的資料是:', data);
	    }catch(e) {

	    }
	}
	window.addEventListener('hashchange', function(e) {
	    console.log('獲得的資料是:', location.hash.substring(1));
        });
    </script>
</body>
</html>

cs2.html 收到訊息後通過 parent.location.hash 值來修改 cs1.html 的 hash 值,從而達到資料傳遞。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>CS2</title>
</head>
<body>
    <script>
    // http://locahost:8081/cs2.html
    switch(location.hash) {
        case "#data":
	    callback();
	    break;
    }
    function callback() {
	const data = "some number: 1111"
	try {
	    parent.location.hash = data;
	}catch(e) {
	    // ie, chrome 下的安全機制無法修改 parent.location.hash
	    // 所以要利用一箇中間的代理 iframe 
	    var ifrproxy = document.createElement('iframe');
		ifrproxy.style.display = 'none';
		ifrproxy.src = 'http://localhost:8080/cs3.html#' + data;     // 該檔案在請求域名的域下
		document.body.appendChild(ifrproxy);
	    }
       }
    </script>
</body>
</html>

由於兩個頁面不在同一個域下IE、Chrome不允許修改parent.location.hash的值,所以要藉助於 localhost:8080 域名下的一個代理 iframe 的 cs3.html 頁面

<script>
    parent.parent.location.hash = self.location.hash.substring(1);
</script>

之後老規矩,開啟兩個 http 伺服器:

img

這裡為了圖方便,將 cs1,2,3 都放在同個資料夾下,實際情況的話 cs1.html 和 cs3.html 要與 cs2.html 分別放在不同的伺服器才對。

之後開啟瀏覽器訪問 localhost:8080/cs1.html,注意不是 8081,就可以看到獲取到的資料了,此時頁面的 hash 值也已經改變。

img

當然這種方法存在著諸多的缺點:

  1. 資料直接暴露在了 url 中
  2. 資料容量和型別都有限等等