摘要: 發個請求也不簡單啊。
- 原文:全面分析前端的網絡請求方式
- 作者:ConardLi
Fundebug經授權轉載,版權歸原作者所有。
一、前端進行網絡請求的關注點
大多數情況下,在前端發起一個網絡請求我們隻需關注下面幾點:
- 傳入基本參數(
,請求方式)url
- 請求參數、請求參數類型
- 設定請求頭
- 擷取響應的方式
- 擷取響應頭、響應狀态、響應結果
- 異常處理
- 攜帶
設定cookie
- 跨域請求
二、前端進行網絡請求的方式
-
表單、form
、重新整理頁面ifream
-
- 異步網絡請求的開山鼻祖Ajax
-
- 一個時代jQuery
-
-fetch
的替代者Ajax
-
等衆多開源庫axios、request
三、關于網絡請求的疑問
-
的出現解決了什麼問題Ajax
- 原生
如何使用Ajax
-
的網絡請求方式jQuery
-
的用法以及坑點fetch
- 如何正确的使用
fetch
- 如何選擇合适的跨域方式
帶着以上這些問題、關注點我們對幾種網絡請求進行一次全面的分析。
四、Ajax 的出現解決了什麼問題
在
Ajax
出現之前,
web
程式是這樣工作的:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiETPwJWZ3ZCMwcTP39zZuBnLuVzRjVXR6hFeGhkYzEzQNpHMT50dwM1T4FkaNZHMyIma1knWxo0VatWNXRWb1MlWuZ0ViBXO5xkNNh0YwIFSh9CXt92YuM3YltWas5iclN3Ztl2Lc9CX6MHc0RHaiojIsJye.png)
這種互動的的缺陷是顯而易見的,任何和伺服器的互動都需要重新整理頁面,使用者體驗非常差,
Ajax
的出現解決了這個問題。
Ajax
全稱
Asynchronous JavaScript + XML
(異步
JavaScript
和
XML
)
使用
Ajax
,網頁應用能夠快速地将增量更新呈現在使用者界面上,而不需要重載(重新整理)整個頁面。
Ajax
本身不是一種新技術,而是用來描述一種使用現有技術集合實作的一個技術方案,浏覽器的
XMLHttpRequest
是實作
Ajax
最重要的對象(
IE6
以下使用
ActiveXObject
)。
盡管
X
在
Ajax
中代表
XML
, 但由于
JSON
的許多優勢,比如更加輕量以及作為
Javascript
的一部分,目前
JSON
的使用比
XML
更加普遍。
五、原生 Ajax 的用法
這裡主要分析
XMLHttpRequest
對象,下面是它的一段基礎使用:
var xhr = new XMLHttpRequest();
xhr.open(\'post\',\'www.xxx.com\',true)
// 接收傳回值
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 ){
if(xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
console.log(xhr.responseText);
}
}
}
// 處理請求參數
postData = {"name1":"value1","name2":"value2"};
postData = (function(value){
var dataString = "";
for(var key in value){
dataString += key+"="+value[key]+"&";
};
return dataString;
}(postData));
// 設定請求頭
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
// 異常處理
xhr.onerror = function() {
console.log(\'Network request failed\')
}
// 跨域攜帶cookie
xhr.withCredentials = true;
// 送出請求
xhr.send(postData);
下面分别對
XMLHttpRequest
對象常用的的函數、屬性、事件進行分析。
函數
open
用于初始化一個請求,用法:
xhr.open(method, url, async);
-
:請求方式,如method
get、post
-
:請求的url
url
-
:是否為異步請求async
send
用于發送
HTTP
請求,即調用該方法後
HTTP
請求才會被真正發出,用法:
xhr.send(param);
-
:http 請求的參數,可以為param
等類型。string、Blob
abort
用于終止一個
ajax
請求,調用此方法後
readyState
将被設定為
,用法:
xhr.abort();
setRequestHeader
用于設定
HTTP
請求頭,此方法必須在
open()
方法和
send()
之間調用,用法:
xhr.setRequestHeader(header, value);
getResponseHeader
用于擷取
http
傳回頭,如果在傳回頭中有多個一樣的名稱,那麼傳回的值就會是用逗号和空格将值分隔的字元串,用法:
var header = xhr.getResponseHeader(name);
屬性
readyState
用來辨別目前
XMLHttpRequest
對象所處的狀态,
XMLHttpRequest
對象總是位于下列狀态中的一個:
值 | 狀态 | 描述 |
---|---|---|
| 代理被建立,但尚未調用 方法。 | |
1 | | 方法已經被調用。 |
2 | | 方法已經被調用,并且頭部和狀态已經可獲得。 |
3 | | 下載下傳中; 屬性已經包含部分資料。 |
4 | | 下載下傳操作已完成。 |
status
表示
http
請求的狀态, 初始值為
。如果伺服器沒有顯式地指定狀态碼, 那麼
status
将被設定為預設值, 即
200
。
responseType
表示響應的資料類型,并允許我們手動設定,如果為空,預設為
text
類型,可以有下面的取值:
值 | 描述 |
---|---|
| 将 設為空字元串與設定為 相同, 是預設類型 (實際上是 )。 |
| 是一個包含二進制資料的 。 |
| 是一個包含二進制資料的 對象 。 |
| response 是一個 或 ,這取決于接收到的資料的 MIME 類型。 |
| 是一個 JavaScript 對象。這個對象是通過将接收到的資料類型視為 解析得到的。 |
| 是包含在 對象中的文本。 |
response
傳回響應的正文,傳回的類型由上面的
responseType
決定。
withCredentials
ajax
請求預設會攜帶同源請求的
cookie
,而跨域請求則不會攜帶
cookie
,設定
xhr
的
withCredentials
的屬性為
true
将允許攜帶跨域
cookie
。
事件回調
onreadystatechange
xhr.onreadystatechange = callback;
當
readyState
屬性發生變化時,callback 會被觸發。
onloadstart
xhr.onloadstart = callback;
在
ajax
請求發送之前(
readyState==1
後,
readyState==2
前),
callback
會被觸發。
onprogress
xhr.onprogress = function(event) {
console.log(event.loaded / event.total);
};
回調函數可以擷取資源總大小
total
,已經加載的資源大小
loaded
,用這兩個值可以計算加載進度。
onload
xhr.onload = callback;
當一個資源及其依賴資源已完成加載時,将觸發
callback
,通常我們會在
onload
事件中處理傳回值。
異常處理
onerror
xhr.onerror = callback;
當
ajax
資源加載失敗時會觸發
callback
。
ontimeout
xhr.ontimeout = callback;
當進度由于預定時間到期而終止時,會觸發
callback
,逾時時間可使用
timeout
屬性進行設定。
推薦大家使用Fundebug,一款很好用的BUG監控工具~
六、jQuery 對 Ajax 的封裝
在很長一段時間裡,人們使用
jQuery
提供的
ajax
封裝進行網絡請求,包括
$.ajax、$.get、$.post
等,這幾個方法放到現在,我依然覺得很實用。
$.ajax({
dataType: \'json\', // 設定傳回值類型
contentType: \'application/json\', // 設定參數類型
headers: {\'Content-Type\',\'application/json\'},// 設定請求頭
xhrFields: { withCredentials: true }, // 跨域攜帶cookie
data: JSON.stringify({a: [{b:1, a:1}]}), // 傳遞參數
error:function(xhr,status){ // 錯誤處理
console.log(xhr,status);
},
success: function (data,status) { // 擷取結果
console.log(data,status);
}
})
$.ajax
隻接收一個參數,這個參數接收一系列配置,其自己封裝了一個
jqXHR
對象,有興趣可以閱讀一下jQuary-ajax 源碼
常用配置:
url
目前頁位址。發送請求的位址。
type
類型:
String
請求方式 (
"POST"
或
"GET"
), 預設為
"GET"
。注意:其它
HTTP
請求方法,如
PUT
和
DELETE
也可以使用,但僅部分浏覽器支援。
timeout
類型:
Number
設定請求逾時時間(毫秒)。此設定将覆寫全局設定。
success
類型:
Function
請求成功後的回調函數。
jsonp
在一個
jsonp
請求中重寫回調函數的名字。這個值用來替代在
"callback=?"
這種
GET
或
POST
請求中
URL
參數裡的
"callback"
部分。
error 類型:
Function
。請求失敗時調用此函數。
注意:源碼裡對錯誤的判定:
isSuccess = (status >= 200 && status < 300) || status === 304;
傳回值除了這幾個狀态碼都會進
error
回調。
dataType
"xml": 傳回 XML 文檔,可用 jQuery 處理。
"html": 傳回純文字 HTML 資訊;包含的 script 标簽會在插入 dom 時執行。
"script": 傳回純文字 JavaScript 代碼。不會自動緩存結果。除非設定了 "cache" 參數。注意:在遠端請求時(不在同一個域下),所有 POST 請求都将轉為 GET 請求。(因為将使用 DOM 的 script标簽來加載)
"json": 傳回 JSON 資料 。
"jsonp": JSONP 格式。使用 JSONP 形式調用函數時,如 "myurl?callback=?" jQuery 将自動替換 ? 為正确的函數名,以執行回調函數。
"text": 傳回純文字字元串
data
類型:
String
使用
JSON.stringify
轉碼
complete
類型:
Function
請求完成後回調函數 (請求成功或失敗之後均調用)。
async
類型:
Boolean
預設值:
true
。預設設定下,所有請求均為異步請求。如果需要發送同步請求,請将此選項設定為
false
。
contentType
類型:
String
預設值:
"application/x-www-form-urlencoded"
。發送資訊至伺服器時内容編碼類型。
鍵值對這樣組織在一般的情況下是沒有什麼問題的,這裡說的一般是,不帶嵌套類型
JSON
,也就是 簡單的
JSON
,形如這樣:
{
a: 1,
b: 2,
c: 3
}
但是在一些複雜的情況下就有問題了。 例如在
Ajax
中你要傳一個複雜的
json
對像,也就說是對象嵌數組,數組中包括對象,你這樣傳:
application/x-www-form-urlencoded
這種形式是沒有辦法将複雜的
JSON
組織成鍵值對形式。
{
data: {
a: [
{
x: 2
}
];
}
}
可以用如下方式傳遞複雜的
json
對象
$.ajax({
dataType: "json",
contentType: "application/json",
data: JSON.stringify({ a: [{ b: 1, a: 1 }] })
});
七、jQuery 的替代者
近年來前端
MV*
的發展壯大,人們越來越少的使用
jQuery
,我們不可能單獨為了使用
jQuery
的
Ajax api
來單獨引入他,無可避免的,我們需要尋找新的技術方案。
尤雨溪在他的文檔中推薦大家用
axios
進行網絡請求。
axios
基于
Promise
對原生的
XHR
進行了非常全面的封裝,使用方式也非常的優雅。另外,
axios
同樣提供了在
node
環境下的支援,可謂是網絡請求的首選方案。
未來必定還會出現更優秀的封裝,他們有非常周全的考慮以及詳細的文檔,這裡我們不多做考究,我們把關注的重點放在更底層的 API
fetch
。
Fetch API
是一個用用于通路和操縱 HTTP 管道的強大的原生 API。
這種功能以前是使用 XMLHttpRequest 實作的。Fetch 提供了一個更好的替代方法,可以很容易地被其他技術使用,例如 Service Workers。Fetch 還提供了單個邏輯位置來定義其他 HTTP 相關概念,例如 CORS 和 HTTP 的擴充。
可見
fetch
是作為
XMLHttpRequest
的替代品出現的。
使用
fetch
,你不需要再額外加載一個外部資源。但它還沒有被浏覽器完全支援,是以你仍然需要一個
polyfill
。
八、fetch 的使用
一個基本的 fetch 請求:
const options = {
method: "POST", // 請求參數
headers: { "Content-Type": "application/json" }, // 設定請求頭
body: JSON.stringify({ name: "123" }), // 請求參數
credentials: "same-origin", // cookie設定
mode: "cors" // 跨域
};
fetch("http://www.xxx.com")
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson); // 響應資料
})
.catch(function(err) {
console.log(err); // 異常處理
});
Fetch API
提供了一個全局的
fetch()
方法,以及幾個輔助對象來發起一個網絡請求。
-
fetch()
fetch()
方法用于發起擷取資源的請求。它傳回一個
promise
,這個
promise
會在請求響應後被
resolve
,并傳回
Response
對象。
-
Headers
可以通過
Headers()
構造函數來建立一個你自己的
headers
對象,相當于
response/request
的頭資訊,可以使你查詢到這些頭資訊,或者針對不同的結果做不同的操作。
var myHeaders = new Headers();
myHeaders.append("Content-Type", "text/plain");
-
Request
通過
Request()
構造函數可以建立一個
Request
對象,這個對象可以作為
fetch
函數的第二個參數。
-
Response
在
fetch()
處理完
promises
之後傳回一個
Response
執行個體,也可以手動建立一個
Response
執行個體。
九、fetch polyfill 源碼分析
由于
fetch
是一個非常底層的
API
,是以我們無法進一步的探究它的底層,但是我們可以借助它的
polyfill
探究它的基本原理,并找出其中的坑點。
代碼結構
由代碼可見,
polyfill
主要對
Fetch
API 提供的四大對象進行了封裝:
fetch 封裝
代碼非常清晰:
- 構造一個
對象并傳回Promise
- 建立一個
對象Request
- 建立一個
對象XMLHttpRequest
- 取出
對象中的請求Request
,請求方發,url
一個open
請求,并将xhr
對象中存儲的Request
取出賦給 xhrheaders
-
後取出xhr onload
的response
、status
、headers
封裝body
對象,調用Response
。resolve
異常處理
可以發現,調用
reject
有三種可能:
- 1.請求逾時
- 2.請求失敗
注意:當和伺服器建立簡介,并收到伺服器的異常狀态碼如
404、500
等并不能觸發
onerror
。當網絡故障時或請求被阻止時,才會标記為
reject
,如跨域、
url
不存在,網絡異常等會觸發
onerror
。
是以使用 fetch 當接收到異常狀态碼都是會進入 then 而不是 catch。這些錯誤請求往往要手動處理。
- 3.手動終止
可以在
request
參數中傳入
signal
對象,并對
signal
對象添加
abort
事件監聽,當
xhr.readyState
變為
4
(響應内容解析完成)後将 signal 對象的 abort 事件監聽移除掉。
這表示,在一個
fetch
請求結束之前可以調用
signal.abort
将其終止。在浏覽器中可以使用
AbortController()
構造函數建立一個控制器,然後使用
AbortController.signal
屬性
這是一個實驗中的功能,此功能某些浏覽器尚在開發中
Headers 封裝
在 header 對象中維護了一個
map
對象,構造函數中可以傳入
Header
對象、數組、普通對象類型的
header
,并将所有的值維護到
map
中。
之前在
fetch
函數中看到調用了
header
的
forEach
方法,下面是它的實作:
可見
header
的周遊即其内部
map
的周遊。
另外
Header
還提供了
append、delete、get、set
等方法,都是對其内部的
map
對象進行操作。
Request 對象
Request
對象接收的兩個參數即
fetch
函數接收的兩個參數,第一個參數可以直接傳遞
url
,也可以傳遞一個構造好的
request
對象。第二個參數即控制不同配置的
option
對象。
可以傳入
credentials、headers、method、mode、signal、referrer
等屬性。
這裡注意:
- 傳入的
被當作headers
構造函數的參數來構造 header 對象。Headers
cookie 處理
fetch 函數中還有如下的代碼:
if (request.credentials === "include") {
xhr.withCredentials = true;
} else if (request.credentials === "omit") {
xhr.withCredentials = false;
}
預設的
credentials
類型為
same-origin
,即可攜帶同源請求的 coodkie。
然後我發現這裡 polyfill 的實作和MDN-使用 Fetch以及很多資料是不一緻的:
mdn: 預設情況下,fetch 不會從服務端發送或接收任何 cookies
于是我分别實驗了下使用
polyfill
和使用原生
fetch
攜帶 cookie 的情況,發現在不設定
credentials
的情況下居然都是預設攜帶同源
cookie
的,這和文檔的說明說不一緻的,查閱了許多資料後都是說
fetch
預設不會攜帶 cookie,下面是使用原生
fetch
在浏覽器進行請求的情況:
然後我發現在已經指出新版浏覽器
credentials
預設值已更改為
same-origin
,舊版依然是
omit
。
确實MDN-使用 Fetch這裡的文檔更新的有些不及時,誤人子弟了…
Response 對象
Response
對象是
fetch
調用成功後的傳回值:
回顧下
f
etch
中對
Response`的操作:
xhr.onload = function() {
var options = {
status: xhr.status,
statusText: xhr.statusText,
headers: parseHeaders(xhr.getAllResponseHeaders() || "")
};
options.url =
"responseURL" in xhr
? xhr.responseURL
: options.headers.get("X-Request-URL");
var body = "response" in xhr ? xhr.response : xhr.responseText;
resolve(new Response(body, options));
};
Response
構造函數:
可見在構造函數中主要對
options
中的
status、statusText、headers、url
等分别做了處理并挂載到
Response
對象上。
構造函數裡面并沒有對
responseText
的明确處理,最後交給了
_initBody
函數處理,而
Response
并沒有主動聲明
_initBody
屬性,代碼最後使用
Response
調用了
Body
函數,實際上
_initBody
函數是通過
Body
函數挂載到
Response
身上的,先來看看
_initBody
函數:
可見,
_initBody
函數根據
xhr.response
的類型(
Blob、FormData、String...
),為不同的參數進行指派,這些參數在
Body
方法中得到不同的應用,下面具體看看
Body
函數還做了哪些其他的操作:
Body
函數中還為
Response
對象挂載了四個函數,
text、json、blob、formData
,這些函數中的操作就是将_initBody 中得到的不同類型的傳回值傳回。
這也說明了,在
fetch
執行完畢後,不能直接在
response
中擷取到傳回值而必須調用
text()、json()
等函數才能擷取到傳回值。
這裡還有一點需要說明:幾個函數中都有類似下面的邏輯:
var rejected = consumed(this);
if (rejected) {
return rejected;
}
consumed 函數:
function consumed(body) {
if (body.bodyUsed) {
return Promise.reject(new TypeError("Already read"));
}
body.bodyUsed = true;
}
每次調用
text()、json()
等函數後會将
bodyUsed
變量變為
true
,用來辨別傳回值已經讀取過了,下一次再讀取直接抛出
TypeError(\'Already read\')
。這也遵循了原生
fetch
的原則:
因為 Responses 對象被設定為了 stream 的方式,是以它們隻能被讀取一次
十、fetch 的坑點
VUE
的文檔中對
fetch
有下面的描述:
使用還有很多别的注意事項,這也是為什麼大家現階段還是更喜歡
fetch
多一些。當然這個事情在未來可能會發生改變。
axios
由于
fetch
是一個非常底層的
API
,它并沒有被進行很多封裝,還有許多問題需要處理:
- 不能直接傳遞
對象作為參數JavaScript
- 需要自己判斷傳回值類型,并執行響應擷取傳回值的方法
- 擷取傳回值方法隻能調用一次,不能多次調用
- 無法正常的捕獲異常
- 老版浏覽器不會預設攜帶
cookie
- 不支援
jsonp
十一、對 fetch 的封裝
請求參數處理
支援傳入不同的參數類型:
function stringify(url, data) {
var dataString = url.indexOf("?") == -1 ? "?" : "&";
for (var key in data) {
dataString += key + "=" + data[key] + "&";
}
return dataString;
}
if (request.formData) {
request.body = request.data;
} else if (/^get$/i.test(request.method)) {
request.url = `${request.url}${stringify(request.url, request.data)}`;
} else if (request.form) {
request.headers.set(
"Content-Type",
"application/x-www-form-urlencoded;charset=UTF-8"
);
request.body = stringify(request.data);
} else {
request.headers.set("Content-Type", "application/json;charset=UTF-8");
request.body = JSON.stringify(request.data);
}
cookie 攜帶
fetch
在新版浏覽器已經開始預設攜帶同源
cookie
,但在老版浏覽器中不會預設攜帶,我們需要對他進行統一設定:
request.credentials = "same-origin"; // 同源攜帶
request.credentials = "include"; // 可跨域攜帶
異常處理
當接收到一個代表錯誤的 HTTP 狀态碼時,從 fetch()傳回的 Promise 不會被标記為 reject, 即使該 HTTP 響應的狀态碼是 404 或 500。相反,它會将 Promise 狀态标記為 resolve (但是會将 resolve 的傳回值的 ok 屬性設定為 false ),僅當網絡故障時或請求被阻止時,才會标記為 reject。
是以我們要對
fetch
的異常進行統一處理
.then(response => {
if (response.ok) {
return Promise.resolve(response);
}else{
const error = new Error(`請求失敗! 狀态碼: ${response.status}, 失敗資訊: ${response.statusText}`);
error.response = response;
return Promise.reject(error);
}
});
傳回值處理
對不同的傳回值類型調用不同的函數接收,這裡必須提前判斷好類型,不能多次調用擷取傳回值的方法:
.then(response => {
let contentType = response.headers.get(\'content-type\');
if (contentType.includes(\'application/json\')) {
return response.json();
} else {
return response.text();
}
});
jsonp
fetch
本身沒有提供對
jsonp
的支援,
jsonp
本身也不屬于一種非常好的解決跨域的方式,推薦使用
cors
或者
nginx
解決跨域,具體請看下面的章節。
fetch 封裝好了,可以愉快的使用了。
嗯,axios 真好用…
十二、跨域總結
談到網絡請求,就不得不提跨域。
浏覽器的同源政策限制了從同一個源加載的文檔或腳本如何與來自另一個源的資源進行互動。這是一個用于隔離潛在惡意檔案的重要安全機制。通常不允許不同源間的讀操作。
跨域條件:協定,域名,端口,有一個不同就算跨域。
下面是解決跨域的幾種方式:
nginx
使用
nginx
反向代理實作跨域,參考我這篇文章:前端開發者必備的 nginx 知識
cors
CORS
是一個
W3C
标準,全稱是”跨域資源共享”
(Cross-origin resource sharing)
。它允許浏覽器向跨源伺服器,發出
XMLHttpRequest
請求。
服務端設定
Access-Control-Allow-Origin
就可以開啟
CORS
。 該屬性表示哪些域名可以通路資源,如果設定通配符則表示所有網站都可以通路資源。
app.all("*", function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
next();
});
jsonp
script
标簽的
src
屬性中的連結可以通路跨域的
js
腳本,利用這個特性,服務端不再傳回
JSON
格式的資料,而是傳回一段調用某個函數的
js
代碼,在
src
中進行了調用,這樣實作了跨域。
jquery
對
jsonp
的支援:
$.ajax({
type : "get",
url : "http://xxxx"
dataType: "jsonp",
jsonp:"callback",
jsonpCallback: "doo",
success : function(data) {
console.log(data);
}
});
fetch、axios
等并沒有直接提供對
jsonp
的支援,如果需要使用這種方式,我們可以嘗試進行手動封裝:
(function(window, document) {
"use strict";
var jsonp = function(url, data, callback) {
// 1.将傳入的data資料轉化為url字元串形式
// {id:1,name:\'jack\'} => id=1&name=jack
var dataString = url.indexof("?") == -1 ? "?" : "&";
for (var key in data) {
dataString += key + "=" + data[key] + "&";
}
// 2 處理url中的回調函數
// cbFuncName回調函數的名字 :my_json_cb_名字的字首 + 随機數(把小數點去掉)
var cbFuncName =
"my_json_cb_" +
Math.random()
.toString()
.replace(".", "");
dataString += "callback=" + cbFuncName;
// 3.建立一個script标簽并插入到頁面中
var scriptEle = document.createElement("script");
scriptEle.src = url + dataString;
// 4.挂載回調函數
window[cbFuncName] = function(data) {
callback(data);
// 處理完回調函數的資料之後,删除jsonp的script标簽
document.body.removeChild(scriptEle);
};
document.body.appendChild(scriptEle);
};
window.$jsonp = jsonp;
})(window, document);
postMessage 跨域
postMessage()
方法允許來自不同源的腳本采用異步方式進行有限的通信,可以實作跨文本檔、多視窗、跨域消息傳遞。
//捕獲iframe
var domain = "http://scriptandstyle.com";
var iframe = document.getElementById("myIFrame").contentWindow;
//發送消息
setInterval(function() {
var message = "Hello! The time is: " + new Date().getTime();
console.log("blog.local: sending message: " + message);
//send the message and target URI
iframe.postMessage(message, domain);
}, 6000);
//響應事件
window.addEventListener(
"message",
function(event) {
if (event.origin !== "http://davidwalsh.name") return;
console.log("message received: " + event.data, event);
event.source.postMessage("holla back youngin!", event.origin);
},
false
);
postMessage
跨域适用于以下場景:同浏覽器多視窗間跨域通信、
iframe
間跨域通信。
WebSocket
WebSocket
是一種雙向通信協定,在建立連接配接之後,
WebSocket
的
server
與
client
都能主動向對方發送或接收資料而不受同源政策的限制。
function WebSocketTest() {
if ("WebSocket" in window) {
alert("您的浏覽器支援 WebSocket!");
// 打開一個 web socket
var ws = new WebSocket("ws://localhost:3000/abcd");
ws.onopen = function() {
// Web Socket 已連接配接上,使用 send() 方法發送資料
ws.send("發送資料");
alert("資料發送中...");
};
ws.onmessage = function(evt) {
var received_msg = evt.data;
alert("資料已接收...");
};
ws.onclose = function() {
// 關閉 websocket
alert("連接配接已關閉...");
};
} else {
// 浏覽器不支援 WebSocket
alert("您的浏覽器不支援 WebSocket!");
}
}
文中如有錯誤,歡迎在評論區指正,謝謝閱讀。