前端初學者都應該知道得跨域通訊方式

跨域是一個咱們常常遇到的問題,特別是當服務有兩個域名的時候,若是你的頁面是在 a.test.com,而後須要向 b.test.com 請求數據,這個時候就存在跨域的問題了。若是你直接請求,你就會發現瀏覽器就直接報錯了,這個時候就要了解什麼是 同源策略 了。javascript

 

什麼是同源策略及其限制

概念

同源策略限制從一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互,這是一個用於隔離潛在惡意文件的關鍵安全機制。css

什麼是源

協議、域名與端口。這三者任何一個不同的話,就算是不一樣源,就會產生跨域的問題。html

什麼是限制

不是一個源的文檔,就沒有權限去操做另外一個源的文檔。前端

Cookie、LocalStorage 和 IndexDB沒法讀取。java

Dom沒法得到node

Ajax請求不能發送jquery

爲何要有同源限制

舉個簡單的例子,咱們登錄是須要帶上 cookie 的,好比咱們登陸了一個銀行網站,那麼咱們的瀏覽器中則會帶上相應的 cookie,下次咱們這個銀行網站的時候,若是這個 cookie 還存在的話,就能夠不須要登陸了;webpack

若是瀏覽器沒有同源策略,那麼一些不法分子能夠拿到咱們瀏覽器的 cookie 從而進咱們的銀行帳號取錢,這是很是可怕的事情。nginx

這就是瀏覽器要有同源策略的緣由。git

常見的跨域場景

URL                                      說明                    是否容許通訊
http://www.domain.com/a.js
http://www.domain.com/b.js         同一域名,不一樣文件或路徑           容許
http://www.domain.com/lab/c.js

http://www.domain.com:8000/a.js
http://www.domain.com/b.js         同一域名,不一樣端口                不容許
 
http://www.domain.com/a.js
https://www.domain.com/b.js        同一域名,不一樣協議                不容許
 
http://www.domain.com/a.js
http://192.168.4.12/b.js           域名和域名對應相同ip              不容許
 
http://www.domain.com/a.js
http://x.domain.com/b.js           主域相同,子域不一樣                不容許
http://domain.com/c.js
 
http://www.domain1.com/a.js
http://www.domain2.com/b.js        不一樣域名                         不容許
複製代碼

 

跨域通訊的幾種方式

由於瀏覽器存在跨域這個問題,可是咱們平時在開發過程當中,有不少場景須要用到跨域通訊,好比前端和後端的接口源不是同一個的時候,這個時候咱們就須要進行跨域通訊了。

  • jsonp
  • CORS:跨域資源共享
  • postMessageHTML5 提供的 api
  • Hash:不一樣源能夠訪問 hash
  • window.name:不一樣的頁面(甚至不一樣域名)加載後依舊存在
  • document.domain:主域相同的狀況
  • websocketHTML5 提供的新的協議
  • webpack:等構建構建工具中提供的跨域解決辦法
  • nginx:這個屬於後端的,筆者在本文就不作詳細介紹了,你們能夠自行找相關資料,其實也蠻簡單的

接下來咱們來詳細講一下這幾種解決跨域問題的通訊方式,全部代碼都會放在這個倉庫中:跨域通訊 示例代碼

JSONP

一般爲了減輕 web 服務器的負載,咱們把 jscssimg 等靜態資源分離到另外一臺獨立域名的服務器上,在html 頁面中再經過相應的標籤從不一樣域名下加載靜態資源,而被瀏覽器容許。

基於此原理,咱們能夠經過動態建立 script,再請求一個帶參網址實現跨域通訊。

案例代碼

咱們新建 jsonp 文件夾,並新建三個文件

  • jsonp.js:封裝了一個簡易的 jsonp 方法,jsonp 其實就是新建一個 script 文件,由於咱們上面也講到了 script 是不會跨域的,接着將須要請求的連接放在 scriptsrc 中,同時在 window 上掛一個全局 callback 方法,後端返回的時候會將你要的數據放在這個 callback 的參數中,這個時候你就能夠獲得這些數據了。
function jsonp ({ url, params, cb }) {
  return new Promise(function(resolve, reject) {
    let script = document.createElement('script');
    // 全局掛一個方法
    window[cb] = function(data) {
      resolve(data);
      document.body.removeChild(script);
    }
    // 將 cb 放在 params 中
    params = { ...params, cb};
    let arrs = [];
    // 將 params 中的 key 拼成相似 a=1 形式 
    for(let key in params) {
      arrs.push(`${key}=${params[key]}`);
    }
    // 開始拼 url,目標結果 a=1&b=2&c=3
    script.src = `${url}?${arrs.join('&')}`;
    document.body.appendChild(script);
  })
}
複製代碼
  • server.js:起一個服務,咱們使用 express 來起一個 Node.js 服務,接下去其餘項目的例子筆者也會使用相似於下面這個 js 來啓動服務,若是是增長方法我會特別標出:
let express = require('express');
let app = express();

// 以當前目錄做爲服務器靜態目錄
// 咱們能夠經過 localhost:3000/index.html 訪問 html 文件
app.use(express.static(__dirname)); 

app.get('/jsonp', function(req, res) {
  // 準備放回給前端的數據
  const data = {
    data: '我是 jsonp 的 data',
  }

  // 獲取前端傳的 cb 方法
  const { cb } = req.query;

  // 設置輸出的 html 格式爲 utf-8
  res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'})

  if (cb) {
    // 若是有 cb 參數,就講 data 返回給前端
    res.end(`${cb}(${JSON.stringify(data)})`);
  } else {
    // 若是沒有 cb,就返回錯誤
    res.end('未傳 callback 參數');
  }
  
})

app.listen(3000, () => {
  console.log('服務器已經在3000端口啓動');
});
複製代碼
  • index.html:jsonp 例子 html 文件,發 jsonp 的請求
...
<body>
  <h1>Jsonp 的 demo</h1>
  <script src="./jsonp.js"></script>
  <script> jsonp({ url: 'http://localhost:3000/jsonp', params: { key: '1', }, cb: 'showData', }).then((data) => { console.log('data', data); }) </script>
</body>
...
複製代碼

咱們在根目錄下運行一下 node ./jsonp/server.js,訪問 localhost:3000

能夠看到在頁面中顯示了 index.html 的內容,同時在控制檯輸出了相應的 data

在 jquery 中使用:

jquery 中已經幫咱們封裝好了,直接使用便可,但須要先引入 jquery,可使用 $.ajax 規定 dataType,具體筆者也不演示了,使用以下:

// ...
$.ajax({
  url: 'http://localhost:3000/jsonp',
  type: 'get',
  dataType: 'jsonp',  // 請求方式爲jsonp
  success:function(ret){
    console.log(ret);
  }
});

複製代碼

一些說明

使用起來很簡單,兼容性好,能兼容一些遠古瀏覽器。可是他只支持 GET 請求,同時會有安全的問題,可能會被 xss 注入。

 

CORS

CORS 是一個 W3C 標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。它容許瀏覽器向跨源發送 XMLHttpRequest,主要在後端服務器中增長一些相關配置便可,但有的時候須要前端進行一些配合,接下來我會講幾個經常使用的配置:

案例代碼

咱們新建 cors 文件夾,並新建四個文件,一個 html 文件,兩個服務器文件,一個本身的服務器文件,一個目標服務器文件,用來咱們作請求使用;一個簡單封裝的 ajax.js 文件:

  • ajax.js

這裏寫了一個簡單的 ajax,具體就不細講了,你們想深刻研究能夠參考:Ajax 知識體系大梳理

function ajax( methods, url ) {
  // 新建一個 xhr 實例
  // 在這裏沒有兼容 IE6, IE5,
  // IE6,IE5 可使用 newActiveXObject("Microsoft.XMLHTTP"); 
  const xhr = new XMLHttpRequest();

  // 初始化一個請求
  // methods:請求類型
  // url:連接
  // async:是否異步,true (異步); false(同步)
  xhr.open(methods, url, true);

  // 只要 readyState 屬性發生變化
  // 狀態變化的監聽函數
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      // 返回成功的時候,輸出相應的 response
      if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
        console.log(xhr.response);
      }
    }
  }  

  xhr.send();
}
複製代碼
  • 兩個 server 文件
server1.js

這個文件和 jsonpserver.js 同樣,在 3000 端口開啓一個服務器,讓咱們的 index.html 可以在 localhost:3000/index.html 訪問。

server2.js

這個文件是目標服務器文件,端口號 4000,用來咱們發送 ajax 請求,這裏咱們增長了一個 getData 方法:

// ...
app.get('/getData', function(req, res) {
  const data = {
    data: '我是 cors 的 data',
  }

  res.end(JSON.stringify(data));  
})
// ...
複製代碼
  • index.html

html 文件用來發送 ajax 請求:

...
<body>
  <h1>Cors demo</h1>
  <script src="./ajax.js"></script>
  <script> ajax('GET', 'http://localhost:4000/getData'); </script>
</body>
...
複製代碼

接下來咱們啓動兩個服務,訪問一下 localhost:3000/index.html

發現有一個報錯:

這時咱們就須要在後端配置 CORS 了。

配置 CORS

咱們能夠修改一下 server2.js,增長 Access-Control-Allow-Origin 這個頭,通常來講咱們改爲 *,就能解決這個問題了,可是在項目中,咱們可能須要設置一個白名單,規定哪些源能夠訪問,咱們能夠作以下配置:

let whiteList = ['http://localhost:3000']

// exprss 的一箇中間件,全部的路由都會走一下
app.all('*', function (req, res, next) {
  // 獲取請求的源
  let origin = req.headers.origin;

  if (whiteList.includes(origin)) {
    // 設置哪一個源訪問
    res.setHeader('Access-Control-Allow-Origin', origin);
  } else {
    // 不在白名單中 返回
    res.end('你不在白名單中');  
  }
  // 繼續執行
  next();
});
複製代碼

這個時候咱們從新運行 server2.js,咱們就能拿到相應的值了:

更多配置

咱們再來介紹幾個經常使用的配置:

Access-Control-Allow-Headers

若是用戶想要在頭部設置一些值,則在後端須要定義這個頭,它也是一個逗號分隔的字符串,代表服務器支持的全部頭信息字段。若是不定義會報以下錯誤:

若是咱們想要在 header 中增長 name=darrell,咱們在 ajax.js 中新增以下代碼:

//...
xhr.open(methods, url, true);
// ...
xhr.setRequestHeader('name', 'darrell'); // 增長一個頭
// ...
xhr.send();
複製代碼

servers.js 增長:

// ...
app.all('*', function (req, res, next) {
  // ...
  if (whiteList.includes(origin)) {
    // 設置哪一個源訪問
    res.setHeader('Access-Control-Allow-Origin', origin);
    // 容許前端攜帶哪一個頭訪問我
    res.setHeader('Access-Control-Allow-Headers', 'name,key');
  }
  // ...
});
複製代碼
Access-Control-Allow-Methods

它的值是逗號分隔的一個字符串,代表服務器支持的全部跨域請求的方法。若是個人請求方法爲 put,不設置次字段會報錯:

server.js 中增長此頭:

app.all('*', function (req, res, next) {
  // ...
  if (whiteList.includes(origin)) {
    // 設置哪一個源訪問
    res.setHeader('Access-Control-Allow-Origin', origin);
    // 容許前端攜帶哪一個頭訪問我
    res.setHeader('Access-Control-Allow-Headers', 'name,key');
    // 容許哪一個方法訪問
    res.header("Access-Control-Allow-Methods","PUT");
  }
  // ...
});
複製代碼
Access-Control-Allow-Credentials

它的值是一個布爾值,表示是否容許發送 Cookie。默認狀況下,Cookie 不包括在 CORS 請求之中。設爲 true,即表示服務器明確許可,Cookie 能夠包含在請求中,一塊兒發給服務器。

若是在瀏覽器端設置了容許攜帶 cookiexhr.withCredentials = true;,那麼必需要在後端設置這個頭,否則會以下錯:

server.js 中增長此頭:

app.all('*', function (req, res, next) {
  // ...
  if (whiteList.includes(origin)) {
    // 設置哪一個源訪問
    res.setHeader('Access-Control-Allow-Origin', origin);
    // 容許前端攜帶哪一個頭訪問我
    res.setHeader('Access-Control-Allow-Headers', 'name,key');
    // 容許哪一個方法訪問
    res.header("Access-Control-Allow-Methods","PUT");
    // 容許攜帶 cookie
    res.header("Access-Control-Allow-Credentials", true);
  }
  // ...
});
複製代碼
Access-Control-Expose-Headers

CORS 請求時,XMLHttpRequest對象的 getResponseHeader() 方法只能拿到6個基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。若是想拿到其餘字段,就必須在 Access-Control-Expose-Headers 裏面指定。

好比我要在 ajaxresponse 中取返回頭中的 name

// ...
if (xhr.readyState === 4) {
  if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
    console.log(xhr.response);
    console.log(xhr.getResponseHeader('name'));
  }
}
// ...
複製代碼

同時在 getData 這個接口寫一個 name 上去:

app.get('/getData', function(req, res) {
  // ...
  res.setHeader('name', 'maolei');
  // ... 
})
複製代碼

若是後端沒有設置次頭,則會有以下報錯:

server.js 中增長此頭:

app.all('*', function (req, res, next) {
  // ...
  if (whiteList.includes(origin)) {
    // 設置哪一個源訪問
    res.setHeader('Access-Control-Allow-Origin', origin);
    // 容許前端攜帶哪一個頭訪問我
    res.setHeader('Access-Control-Allow-Headers', 'name,key');
    // 容許哪一個方法訪問
    res.header("Access-Control-Allow-Methods","PUT");
    // 容許攜帶 cookie
    res.header("Access-Control-Allow-Credentials", true);
    // 容許前端獲取哪一個頭
    res.header("Access-Control-Expose-Headers", 'name');
  }
  // ...
});
複製代碼
Access-Control-Max-Age

用來指定本次預檢請求的有效期,單位爲秒。預檢請求指的是瀏覽器針對於複雜請求的時候,會在正式通訊以前,增長一次HTTP查詢請求(option),稱爲 預檢請求。咱們能夠設置這個請求的有效期:

app.all('*', function (req, res, next) {
  // ...
  if (whiteList.includes(origin)) {
    // 設置哪一個源訪問
    res.setHeader('Access-Control-Allow-Origin', origin);
    // 容許前端攜帶哪一個頭訪問我
    res.setHeader('Access-Control-Allow-Headers', 'name,key');
    // 容許哪一個方法訪問
    res.header("Access-Control-Allow-Methods","PUT");
    // 容許攜帶 cookie
    res.header("Access-Control-Allow-Credentials", true);
    // 容許前端獲取哪一個頭
    res.header("Access-Control-Expose-Headers", 'name');
    // 用來指定本次預檢請求的有效期,單位爲秒
    res.header("Access-Control-Max-Age","6");
  }
  // ...
});
複製代碼

更多的請求,筆者在這裏就不講了,你們能夠參考阮一峯老師的 跨域資源共享 CORS 詳解

一些說明

CORS 請求是比較安全的,並且支持全部類型的 HTTP 請求,配置起來也相對比較方便;開發者可使用普通的 XMLHttpRequest 發起請求和得到數據,比起 JSONP 有更好的錯誤處理;並且在絕大多數現代瀏覽器都已經支持了 CORS,若是業務中不須要兼容遠古瀏覽器的,就能夠放心的使用 CORS 了。

 

postMessage

postMessageHTML5 中的API,且是爲數很少能夠跨域操做的 window 屬性之一,它可用於解決如下方面的問題

  • 頁面和其打開的新窗口的數據傳遞,能夠經過 opener 來進行通行。
  • 多窗口之間消息傳遞
  • 頁面與嵌套的 iframe 消息傳遞
  • 上面三個場景的跨域數據傳遞

這裏咱們就來舉一個 iframe 嵌套的例子:

案例代碼

咱們新建 postmessage 文件夾,並新建四個文件,兩個不一樣源 html 文件,兩個服務器文件,一個本身的服務器文件,一個目標服務器文件,用來咱們作請求使用;

  • 兩個 html

a.html 中新建一個 iframe,其 srchttp://localhost:4000/b.html

<!-- a.html -->
...
<body>
  <h1>postmessage A 頁面</h1>
  <iframe src="http://localhost:4000/b.html" id="frame" frameborder="10" onload="load()" ></iframe>
</body>
...

<!-- b.html -->
...
<body>
  <h1>postmessage B 頁面</h1>
</body>
...
複製代碼
  • 兩個 server 文件

兩個 server 文件和 CORS 同樣,只不過都是開啓兩個簡單的服務而已。

需求

我麼想要從 a 頁面發送一些信息給 b 頁面,同時 b 在收到消息後在回信給 a 頁面。

咱們能夠修改 a.html,補充 iframeload 函數,咱們在 a 頁面發送咱們定義好的信息給 b 頁面,同時監聽 b 頁面回發過來的信息,具體配置可參考 postMessge 官方文檔

...
<script> function load() { let frame = document.getElementById("frame"); var iwindow = frame.contentWindow; // 發送 postMessage 信息 // 第一個:要傳的數據 // 第二個:指定源 iwindow.postMessage({ type: 'a', data: "我是 a 發的消息", }, "http://localhost:4000") } // 監聽 b 頁面發過來的信息 window.addEventListener('message', function(event){ // 拿到數據,判斷是否是從 b 發過來的 // 若是不是 直接 return const { type, data } = event.data; if (!event.data || type !== 'b') { return; } console.log(data); }) </script>
...
複製代碼

b 中監聽 a 發過來的信息:

...
<script> window.addEventListener('message', function(event){ console.log(event.data.data); event.source.postMessage({ type: 'b', data: "我是 b 發的消息", }, event.origin); }) </script>
...
複製代碼

咱們開啓兩個服務,在頁面中打開 localhost:3000/a.html,能夠看到信息打印了出來:

 

Hash

不一樣域之間能夠經過 window.location.hash 進行傳遞相應的信息,在同域名直接能夠直接經過 js 訪問來設置:

案例代碼

咱們新建 hash 文件夾,並新建五個文件,三個 html 文件,兩個同源 html,一個不一樣源 html,兩個服務器文件,一個本身的服務器文件,一個目標服務器文件,用來咱們作請求使用,服務器文件和以前同樣,就不列舉了。

  • 三個 html 文件

兩個同源 htmlab 文件,在 3000 端口下,另一個 c4000 端口下:

<!-- a.html -->
...
<body>
  <!-- 路徑後的 hash 能夠用來通訊 -->
  <!-- a 訪問 c -->
  <h1>window-hash a.html</h1>
  <iframe src="http://localhost:4000/c.html#hashname=darrell" id="frame" frameborder="10" ></iframe>
</body>
...

<!-- b.html -->
...
<body>
  <h1>window-hash b.html</h1>
</body>
...

<!-- c.html -->
...
<body>
  <h1>window-hash c.html</h1>
</body>
...
複製代碼

需求:

完成 ac 頁面的通訊過程,在 a.html 中的 iframec.html 發送 #hashname=darrellc 取到以後在發送相關的信息給 aa 在進行相應的處理。

其實很簡單,咱們在 c 獲得 a 發過來的信息以後在新建 iframe 指向一個和a 同源的 b,做爲一箇中轉站,由於 ab 同源,接着就能夠直接經過 js 訪問了,咱們修改一下 html 代碼:

<!-- a.html -->
...
<body>
  <!-- 路徑後的 hash 能夠用來通訊 -->
  <!-- a 訪問 c -->
  <h1>window-hash a.html</h1>
  <iframe src="http://localhost:4000/c.html#hashname=darrell" id="frame" frameborder="10" ></iframe>
  <script> // 監聽 b 修改 hashchange window.onhashchange = function() { console.log('a 收到', location.hash); } </script>
</body>
...

<!-- b.html -->
...
<body>
  <h1>window-hash b.html</h1>
  <script> // 收到 c 發過來的 hash console.log('b 收到', location.hash); window.parent.parent.location.hash = window.location.hash; </script>
</body>
...

<!-- c.html -->
...
<body>
  <h1>window-hash c.html</h1>
  <script> // 收到 a 發過來的 hash console.log('c 收到', window.location.hash); let iframe = document.createElement('iframe'); iframe.src = 'http://localhost:3000/b.html#name=c'; document.body.appendChild(iframe); </script>
</body>
...
複製代碼

咱們開啓兩個服務,在頁面中打開 localhost:3000/a.html,能夠看到信息打印了出來:

 

window.name

window.name 有一個特性,就是 name 值在不一樣的頁面(甚至不一樣域名)加載後依舊存在。所以咱們也可使用其幫助咱們解決跨域問題。

案例代碼

咱們新建 name 文件夾,並新建五個文件,代碼和 hash 的文件代碼同樣。

<!-- a.html -->
...
<body>
  <h1>window-name a.html</h1>
  <iframe src="http://localhost:4000/c.html#hashname=darrell" id="frame" frameborder="10" ></iframe>
</body>
...

<!-- b.html -->
...
<body>
  <h1>window-name b.html</h1>
</body>
...

<!-- c.html -->
...
<body>
  <h1>window-name c.html</h1>
  <script> window.name = '我是來自 c 的信息'; </script>
</body>
...
複製代碼

需求也相似,我須要在 a 獲取到 c 這個頁面進行定義的 window.name,其實很簡單咱們只要經過 b 這個中間頁面進行數據傳遞就好了,當咱們加載完成 c 頁面這個 iframe 之後,再從新將 src 賦值爲 http://localhost:3000/b.html,利用 name 的特性,這個時候 a 頁面就能夠經過 window.name 拿到 c 中的 name 了,咱們只須要修改 a.html 文件就能夠了:

<!-- a.html -->
...
<body>
  <h1>window-name a.html</h1>
  <iframe src="http://localhost:4000/c.html" id="frame" frameborder="10" onload="load()" ></iframe>
  <script> let first = true; function load() { let frame = document.getElementById("frame"); let iwindow = frame.contentWindow; if (first) { frame.src = 'http://localhost:3000/b.html'; first = false; } else { console.log(iwindow.name); } } </script>
</body>
...
複製代碼

咱們開啓兩個服務,在頁面中打開 localhost:3000/a.html,能夠看到信息打印了出來:

 

document.domain

在兩個域名是同一個主域的時候,若是須要進行跨域通訊,咱們能夠將兩個頁面都經過 js 強制設置document.domain 爲基礎主域,這就實現了同域。

案例代碼

咱們新建 domain 文件夾,並新建四個文件,兩個不一樣源的 html,兩個服務文件和 name 相同:

咱們配置一下系統的 host 文件,將 127.0.0.1 配置出兩個虛擬域名:a.example.cnb.example.cn

這樣咱們就能使用 a.example.cn:3000/a.htmlb.example.cn:4000/b.html 來訪問兩個 html 文件了,

由於兩個域名的主域相同,咱們能夠設置 document-domain=example.cn 來實現:

<!--a.html-->
...
<body>
  <!-- 配置 host -->
  <!-- http://a.example.cn:3000/a.html -->
  <h1>document-domain a.html</h1>
  <iframe src="http://b.example.cn:3000/b.html" id="frame" frameborder="10" onload="load()" ></iframe>
  <script> document.domain = "example.cn"; function load() { let frame = document.getElementById("frame"); let iwindow = frame.contentWindow; console.log(iwindow.name); } </script>
</body>
...

<!--b.html-->
...
<body>
  <h1>document-domain b.html</h1>
  <script> document.domain = "example.cn"; var name = 'b 定義的 name'; </script>
</body>
...
複製代碼

咱們開啓兩個服務,在頁面中打開 http://a.example.cn/:3000/a.html,能夠看到信息打印了出來:

一些說明

須要注意的是此方案只能在主域相同,子域不一樣的跨域應用場景。其餘狀況下不能使用。

 

webSocket

WebSocketHTML5 一種新的協議。它實現了瀏覽器與服務器全雙工通訊,同時容許跨域通信。

咱們先用原生的 WebSocket來舉個例子,以後再使用 Socket.io 來舉個例子:

原生 WebSocket

咱們新建 websocket 文件夾,新建 socket.htmlserver.js 文件:

  • socket.html

咱們建立一個 socket 實例,並向服務端發送一些消息,同時監聽服務端發送給客戶端的消息。

...
<body>
  <h1>Socket demo</h1>
  <script> let socket = new WebSocket("ws://localhost:3000"); socket.onopen = function() { socket.send('瀏覽器發出的內容'); } socket.onmessage = function(e) { console.log(e.data); } </script>
</body>
...
複製代碼
  • server.js

咱們安裝一個依賴 ws,用來處理 websocket,接着起一個 websocket 服務器,監聽客戶端的信息,同時發送相應的信息出到客戶端。

let express = require('express');
let WebSocket = require('ws');
let wss = new WebSocket.Server({ port: 3000 });

wss.on('connection', function(ws) {
  ws.on('message', function(data) {
    console.log(data);

    ws.send('服務器發的內容');
  })
})
複製代碼

咱們開啓一下服務,這個時候咱們能夠直接打開 file 協議的 socket.html,咱們能夠在控制檯看到服務器發送過來的信息,同時也能夠在服務端開到客戶端發送過來的信息:

 

sockit.io

由於原生 WebSocket API 不是特別好用,兼容性也很差,咱們可使用 Socket.io,它很好地封裝了 webSocket 接口,提供了更簡單、靈活的接口。同時對不支持 Websocket 的瀏覽器作了向下兼容。

咱們這個例子參考了官方的一個 簡易聊天室,同時咱們須要安裝一下 socket.io

npm install socket.io
複製代碼
html 代碼
<!-- 引入 socketIO.html -->
...
<body>
  <ul id="messages"></ul>
    <form action="">
      <input id="m" autocomplete="off" /><button>Send</button>
    </form>
  </ul>

  <!-- 引入 socket.io 和 jquery 的 cdn -->
  <script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
  <script src="https://code.jquery.com/jquery-1.11.1.js"></script>

  <script> $(function () { //創建一個socket var socket = io(); $('form').submit(function(){ //發送socket到服務器。 socket.emit('chat message', $('#m').val()); $('#m').val(''); return false; }); socket.on('chat message', function(msg){ //接受服務器傳回來的數據,新建一個li標籤 $('#messages').append($('<li>').text(msg)); window.scrollTo(0, document.body.scrollHeight); }); }); </script>
</body>
...
複製代碼
serverIO.js
var app = require('express')();
var http = require('http').createServer(app);
var io = require('socket.io')(http);
var port = process.env.PORT || 3000;

app.get('/', function(req, res){
  res.sendFile(__dirname + '/socketIO.html');
});

io.on('connection', function(socket){
  // 監聽socket是否鏈接,若是鏈接的話,就發送數據,chat-message
  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
  });

  // 監聽socket是否斷開,斷開時執行相應的方法
  socket.on('disconnect', function(){
    console.log('user disconnected');
  }); 
});

http.listen(port, function(){
  console.log('listening on *:' + port);
});
複製代碼

咱們啓動一下服務:node ./websocket/serverIO.js:咱們能夠在兩個頁面互發消息,以下圖:

 

webpack 中的 devServer

若是你在你的項目中使用了 webpack,那麼 webpackdevServer 中提供了相應的配置參數,你只要在其中進行配置便可:

module.exports = {
  ...
  devServer: {
    // 代理
    proxy: {
      '/movie/': {
      	target: 'https://douban.uieee.com/v2',
        // 是否能夠容許跨域
      	changeOrigin: true,
      	headers: {
          host:'www.example.org',
          cookie: 'isLogin=1' // 判斷是否登陸的 cookie 信息
        }
      }
    }
  }
};
複製代碼

更多的關於 webpackdevServer 的配置,你們能夠參考筆者寫的 webpack-dev-server 高級配置,在這裏就不細講了。

 

一些說明

文章參考了 前端常見跨域解決方案(全),同時加入了本身的思考和代碼示例,但願你們看完以後,能對跨域通訊的解決辦法瞭然於胸。

 

相關資料

 

示例代碼

相關文章
相關標籤/搜索