瀏覽器上下文通信

文章目錄

  • 瀏覽器上下文通信
    • 同源通信
      • 同源通信流程
        • 同一瀏覽器上下文通信
        • 不同瀏覽器上下文通信
    • 跨域通信
      • 前端和前端跨域
      • 前端和后端跨域

瀏覽器上下文通信

瀏覽器上下文通信分為兩種:同源和跨源。同樣的同源通信也分為同一瀏覽器上下文和不同的瀏覽器上下文。

同源通信

同源通信方式多種多樣,最常用的應該就是localStorage, sessionStorage,也有一個全局對象或者模塊導出的對象,還有cookie, caches(cacheStorage)等,以及通過location對象,history.state, Event API, Messaging API, SharedWorker API等方式傳遞消息。

同源通信流程

通信流程大體分為兩步:第一步是傳遞數據,第二步是通知目標

同一瀏覽器上下文通信

第一步很容易,上面的方法都可以。但是第二步就有點不同的。有些方法提供了官方的事件監聽,比如storage, hashchange, popstate等,但有些是沒有這些原生事件的,那就需要一種方式通知。

很容易想到的就是自定義事件(CustomEvent)

el.addEventListener('cookiechange', (e) => {console.log(e.detail);// ...
});const event = new CustomEvent('cookiechange', {detail: {getRandom(min, max) {return Math.floor(Math.random() * (max - min + 1)) + min;}},bubbles: true, // 可以調用 stopPropagationcancelable: true, // 可以調用 preventDefault
});// 更新操作后通知對應元素或其子元素(要設置冒泡)
el.dispatchEvent(event);

除了這種方式,還有一種常見的就是發布訂閱

class PubSub {constructor() {this.subs = new Map() // 訂閱者}// 訂閱on(type, fn) {if (!this.subs.has(type)) {this.subs.set(type, new Set())}this.subs.get(type).add(fn)}// 發布emit(type, ...args) {if (this.subs.has(type)) {for (const fn of this.subs.get(type)) {fn(...args)}}}
}

最后一個就是window.postMessage,可以先監聽message事件,然后在需要時通知。

window.addEventListener('message', (e) => {console.log(e);
});// 在需要時調用
window.postMessage({msg: '這是發給自己的消息',type: 'postMessage'},'*');
不同瀏覽器上下文通信

上面的方式都要求同一瀏覽器上下文,也就是處于同一個window下的文檔,這屬于內部通信。如果是不同的上下文,比如使用iframe或兩個標簽頁,雖然是同源的但這樣就無法通知到,這就需要跨瀏覽器上下文通信。

postMessage,Broadcast Channel API,Channel Messaging API, SharedWorker API都可以實現。
最簡單的就是使用BroadcastSharedWorker(需要瀏覽器支持),其它兩種更常用于跨域通信。可以點擊上面的鏈接查看用法。

跨域通信

跨域通信也有很多類型,前端和前端跨域前端和后端跨域后端和后端跨域。后端和后端那就是純后端的處理,和瀏覽器沒什么關系了,除非使用了類似web socket這種長連接。

前端和前端跨域

想要實現兩個跨域的前端頁面通信,一方必須知道另一方的瀏覽器上下文或者消息句柄,也就是兩個瀏覽器上下文必須要存在聯系。比如使用<iframe>包含另一個上下文或者使用window.open打開另一個上下文。

需要注意的是,這兩種方式都需要等待新的上下文加載完成之后才能通信的。所以<iframe>需要添加load事件監聽,而window.open可以讓對方先發送消息建立連接,類似TCP三次握手之后才正式發送消息。

// localhost:3000/a.html 向 localhost:4000/b.html 通信
// localhost:3000/a.html
const messageCallback = (e) => {if (e.origin === 'http://localhost:4000') {//接受返回數據console.log(e.data)}
}
let active = null;
const origin = 'http://localhost:4000';
frame.addEventListener('load', () => {window.addEventListener('message', messageCallback)active = frame.contentWindow;active.postMessage({type: 'init',msg: 'hello'},origin) //發送數據
})
frame.addEventListener('beforeunload', () => {window.removeEventListener('message', messageCallback)
})
// 發送消息函數
const postMsg = (payload) => {if (active && !active.closed) {active.postMessage(payload, origin);}
}// localhost:4000/b.html
let active = null;
let origin = '';
window.postMessage('message', (e) => {if (e.data && e.data.type === 'init') {active = e.source;origin = e.origin;return;}// 處理其它消息
});
const postMsg = (payload) => {// ...
}// 如果使用的是 window.open
let active = null;
let origin = '';
window.addEventListener('message', (e) => {// 處理初始化消息if (e.data === 'B_LOADED') {active = e.source;origin = e.origin;active.postMessage('A_ACCEPTED', origin);  // 第三步,完成之后可以正常通信了return;}// 處理其它消息
})
xxx.addEventListener('click', () => {// 默認創建標簽頁,如果傳遞第三個參數或`target: '_blank'`會創建新的窗口window.open('http://localhost:4000/b.html', 'b')  // 第一步// 這里創建之后可能頁面還沒加載完成,所以調用 postMessage 可能沒有響應。// 要么使用定時器不斷輪詢,要么等待對方加載完成之后再建立連接。
})
const postMsg = (payload) => {// ...
}// localhost:4000/b.html
let active = null;
let origin = 'http://localhost:3000';
window.addEventListener('message', (e) => {if (e.data && e.data === 'A_ACCEPTED') {// 建立連接成功return;}// 處理其它消息
})
const postMsg = (payload) => {// ...
}
if (window.opener && !window.opener.closed) {active = window.opener;postMsg('B_LOADED'); // 第二步
}

這里使用的一般是上面的postMessageChannel Messaging API,可以點擊上面的鏈接查看使用示例。

前端和后端跨域

前后端跨域是非常常見的,通用的解決方案一般使用使用JSONPnew Image需要使用callback作為參數的查詢字符串,缺點就是只能發起GET請求。

另一種需要滿足CORS的請求規范,也就是前后端同時遵守一套跨域規則,滿足之后就允許跨域請求了。

一些普通的GET請求可以直接請求,但其它方法的請求需要先發起預檢請求約定一些規則。

// server.js
import express from 'express'
const app = express()const whiteList = ['http://localhost:3000'] //設置白名單
// 設置跨域插件
app.use(function (req, res, next) {const origin = req.headers.originif (whiteList.includes(origin)) {// 設置哪個源可以訪問我res.setHeader('Access-Control-Allow-Origin', origin)// 允許攜帶哪個頭訪問我res.setHeader('Access-Control-Allow-Headers', 'Access-Token')// 允許哪個方法訪問我res.setHeader('Access-Control-Allow-Methods', 'POST,PUT,DELETE')// 允許攜帶cookieres.setHeader('Access-Control-Allow-Credentials', true)// 預檢的存活時間res.setHeader('Access-Control-Max-Age', 24 * 60 * 60 * 1000)// 允許返回的頭res.setHeader('Access-Control-Expose-Headers', 'latest-version')}next()
})
app.put('/getData', function (req, res) {console.log(req.headers)res.setHeader('latest-version', '3.2.1') // 返回一個響應頭res.end('over');
})
app.listen(4000);

這個后端要求跨域請求的源必須是白名單里的,同時規定了請求需要使用的方法,跨域請求需要傳遞的請求頭以及一些其它配置。只要用戶滿足請求方法和請求頭的要求就可以請求http://localhost:4000/getData,這個方法設置了響應頭latest-version和響應數據over

前端需要根據需求發起跨域請求。

// XMLHttpRequest
const xhr = new XMLHttpRequest()
xhr.withCredentials = true // 前端設置是否帶cookie
xhr.open('PUT', 'http://localhost:4000/getData', true)
xhr.setRequestHeader('Access-Token', '123456')
xhr.onreadystatechange = function () {if (xhr.readyState === 4) {if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {//得到響應頭console.log(xhr.getResponseHeader('latest-version')) // 3.2.1console.log(xhr.response) // over}}
}
xhr.send();// fetch
fetch('http://localhost:4000/getData', {mode: 'cors', // 跨域credentials: 'include', // 攜帶cookieheaders: {'Access-Token': '123456' // 這個自定義請求頭是后臺要求的},method: 'PUT' // 請求方式
}).then((res) => {for (const [key, value] of res.headers.entries()) {if (key === 'latest-version') {console.log(value) // 3.2.1}}return res.text()}).then((data) => {console.log(data) // over})

這樣前端和后端的跨域請求就完成了,只要過期時間沒到,再次跨域請求就不需要發起預檢請求了。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/77712.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/77712.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/77712.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux 離線部署 Docker 18.06.3 終極指南(附一鍵安裝卸載腳本)

Linux 離線部署 Docker 18.06.3 終極指南&#xff08;附一鍵安裝/卸載腳本&#xff09; 摘要&#xff1a;本文針對無外網環境的 Linux 服務器&#xff0c;提供基于二進制包的 Docker 18.06.3 離線安裝全流程指南。包含自動化腳本設計、服務配置優化及安全卸載方案&#xff0c;…

【前端】跟著maxkb學習logicflow流程圖畫法

文章目錄 背景1. 選定學習對象-maxkb應用邏輯編排2. 確定實現框架3. 關鍵邏輯&#xff1a;查看app-node.js4. 學習開始節點繪制流程數據形式 5. 給節點增加表單輸入框遇到過的問題 背景 看看前端如何繪制流程圖&#xff0c;界面好看點。 "logicflow/core": "1.…

Android 12系統靜態壁紙深度定制指南

1. 需求背景與實現原理 在Android 12系統ROM定制開發中&#xff0c;擴展靜態壁紙功能需要深入理解WallpaperManagerService的架構體系。系統壁紙管理通過雙端協作實現&#xff1a; WallpaperManagerService&#xff08;frameworks層&#xff09;&#xff1a;負責壁紙狀態管理、…

相得益彰 — 基于 GraphRAG 事理圖譜驅動的實時金融行情新聞資訊洞察

*本文為亞馬遜云科技博客文章&#xff0c;僅用于技術分享&#xff0c;不構成投資建議或金融決策支持。文中涉及的公司名稱僅用于技術示例&#xff0c;不代表亞馬遜云科技觀點或與這些公司的商業合作關系。 背景介紹 在當今這個信息爆炸的時代&#xff0c;金融市場每天都在產生…

OpenCV---圖像預處理(四)

OpenCV—圖像預處理&#xff08;四&#xff09; 文章目錄 OpenCV---圖像預處理&#xff08;四&#xff09;九&#xff0c;圖像掩膜9.1 制作掩膜9.2 與運算9.3 顏色替換9.3.19.3.2 顏色替換 十&#xff0c;ROI切割十 一&#xff0c;圖像添加水印11.1模板輸入11.2 與運算11.3 圖像…

【MySQL】:數據庫事務管理

一&#xff1a;學習路徑 &#xff08;1&#xff09;下載安裝mysql &#xff08;2&#xff09;學習語言&#xff1a;SQL(操作數據庫&#xff09; &#xff08;3&#xff09;mysql集群&#xff08;提升數據庫存儲效率&#xff09; &#xff08;4&#xff09;SQL使用&#xff0c;M…

內存函數和動態內存管理

目錄 一、memcpy庫函數介紹 1. memcpy的使用 2. memcpy的模擬 二、memmove庫函數介紹 1. memmove的使用 2. memmove的模擬 三、memset庫函數介紹 四、memcmp庫函數介紹 五、動態內存中malloc和free 1. malloc 2. free 六、動態內存中calloc和realloc 1. calloc 2. realloc 七、…

yarn的基本介紹

1.Hadoop的三大結構及各自的作用&#xff1a; Hadoop是一個開源的分布式計算框架&#xff0c;它主要包括三大核心組件&#xff1a;HDFS&#xff08;Hadoop Distributed File System&#xff09;、YARN&#xff08;Yet Another Resource Negotiator&#xff09;和MapReduce。以…

STM32的啟動方式

目錄 一、從主閃存存儲器啟動&#xff08;Main Flash Memory&#xff09; 二、從系統存儲器啟動&#xff08;System Memory&#xff09; 三、從內置SRAM啟動&#xff08;Embedded SRAM&#xff09; 四、從外掛存儲介質啟動的實現方式 1. 存儲介質選型 2. 硬件連接 3. 引…

STC定時器頻率占空比程序

// // 一、宏定義區 // #include <STC15.H> //頭文件 #include <intrins.h> //庫函數文件 #define FOSC 12000000L //IRC頻率 typedef …

數據庫服務器架構

ORM ORM&#xff08;Object Relational Mapping&#xff09;&#xff1a;對象與關系數據之間的映射 映射關系表&#xff1a; 類&#xff08;class&#xff09;—— 數據庫的表&#xff08;table&#xff09; 對象&#xff08;object&#xff09;——記錄&#xff08;record…

【論文速遞】2025年04周 (Robotics/Embodied AI/LLM)

目錄 DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning摘要 Evolving Deeper LLM Thinking摘要 Kimi k1.5: Scaling Reinforcement Learning with LLMs摘要 Agent-R: Training Language Model Agents to Reflect via Iterative Self-Train…

FortiAI 重塑Fortinet Security Fabric全面智能化進階

專注推動網絡與安全融合的全球性綜合網絡安全解決方案供應商 Fortinet&#xff08;NASDAQ&#xff1a;FTNT&#xff09;&#xff0c;近日宣布&#xff0c;旗下 Fortinet Security Fabric 安全平臺成功嵌入了 FortiAI 關鍵創新功能。這一舉措將有效增強用戶對各類新興威脅的防護…

汽車免拆診斷案例 | 2019款大眾途觀L車鼓風機偶爾不工作

故障現象 一輛2019款大眾途觀L車&#xff0c;搭載DKV發動機和0DE雙離合變速器&#xff0c;累計行駛里程約為8萬km。車主進廠反映&#xff0c;鼓風機偶爾不工作。 故障診斷  接車后試車&#xff0c;鼓風機各擋位均工作正常。用故障檢測儀檢測&#xff0c;空調控制單元&#x…

MySQL為什么默認使用RR隔離級別?

大家好&#xff0c;我是鋒哥。今天分享關于【MySQL為什么默認使用RR隔離級別?】面試題。希望對大家有幫助&#xff1b; MySQL為什么默認使用RR隔離級別? 1000道 互聯網大廠Java工程師 精選面試題-Java資源分享網 MySQL 默認使用 RR&#xff08;Repeatable Read&#xff09;…

目標檢測篇---R-CNN梳理

目標檢測系列文章 第一章 R-CNN 目錄 目標檢測系列文章&#x1f4c4; 論文標題&#x1f9e0; 論文邏輯梳理1. 引言部分梳理 (動機與思想) &#x1f4dd; 三句話總結&#x1f50d; 方法邏輯梳理&#x1f680; 關鍵創新點&#x1f517; 方法流程圖補充邊界框回歸 (BBR)1. BBR 的…

Java技術棧 —— 基本規范

Java技術棧 —— 基本規范 一、接口文檔生成工具二、接口設計2.1 開發順序2.2 接口規范 三、數據類封裝 一、接口文檔生成工具 有很多jar包都支持swagger的接口文檔&#xff0c;這樣方便了接口測試&#xff0c;不需要用apifox自己寫接口&#xff0c;直接調用文檔里的swagger接…

Django ORM 定義模型

提示&#xff1a;定義模型字段的類型 文章目錄 一、字段類型二、字段屬性三、元信息 一、字段類型 常用字段 字段名描述備注AutoFieldint 自增必填參數 primary_keyTrue&#xff0c;無該字段時&#xff0c;django自動創建一個 BigAutoField&#xff0c;一個model不能有兩個Au…

[密碼學基礎]GB與GM國密標準深度解析:定位、差異與協同發展

[密碼學基礎]GB與GM國密標準深度解析&#xff1a;定位、差異與協同發展 導語 在國產密碼技術自主可控的浪潮下&#xff0c;GB&#xff08;國家標準&#xff09;與GM&#xff08;密碼行業標準&#xff09;共同構建了我國商用密碼的技術規范體系。二者在制定主體、法律效力、技術…

Day-1 漏洞攻擊實戰

實訓任務1 漏洞攻擊實戰一 使用 御劍 得到網站后臺地址 數據庫登錄與日志配置?? 使用默認密碼 root:root 登錄phpMyAdmin&#xff0c;執行 SHOW VARIABLES LIKE general% 查看日志狀態。 開啟日志功能&#xff1a;set global general_log "ON";&#xff08;配圖&…