BOM✅
如何实现浏览器内多个标签页之间的通信?
在浏览器内多个标签页之间实现通信可以通过以下几种方式:
-
使用 Broadcast Channel API:Broadcast Channel API 是 HTML5 提供的一种跨页面通信的机制。通过该 API,可以在不同的标签页之间发送消息,实现实时的双向通信。
-
使用 LocalStorage 或 SessionStorage:LocalStorage 和 SessionStorage 是浏览器提供的本地存储机制。可以通过在一个标签页中修改 LocalStorage 或 SessionStorage 中的数据,然后在其他标签页中监听该数据的变化,实现跨标签页的通信。
-
使用 SharedWorker:SharedWorker 是一种特殊的 Web Worker,可以被多个浏览器标签页所共享。通过 SharedWorker,不同标签页可以通过消息传递进行通信。
-
使用 Cookies:通过设置同一个域名下的 Cookie,不同的标签页可以共享这些 Cookie 数据。可以在一个标签页中设置 Cookie,然后在其他标签页中读取该 Cookie 实现通信。
-
使用 Window.postMessage:Window.postMessage 方法可以在不同的浏览器窗口之间进行跨域通信。可以通过在一个窗口中使用 postMessage 方法向其他窗口发送消息,接收窗口通过监听 message 事件来接收并处理消息。
Broadcast Channel API
Broadcast Channel API 是 HTML5 提供的一种跨页面通信的机制,它可以在同一个域名下的多个浏览器标签页之间进行实时的双向通信。
通过 Broadcast Channel API,你可以创建一个通道(channel),然后不同的标签页可以通过这个通道发送和接收消息。每个标签页都可以监听通道中的消息,并对接收到的消息做出相应的处理。
使用 Broadcast Channel API 实现多页签之间的通信的步骤如下:
- 创建一个 BroadcastChannel 对象,并指定一个唯一的通道名称:
const channel = new BroadcastChannel('channelName')
- 在一个标签页中发送消息:
channel.postMessage('message')
- 在其他标签页中监听消息并做出响应:
channel.addEventListener('message', event => {
const message = event.data
// 处理接收到的消息
})
通过 Broadcast Channel API,不同的标签页可以实时地收发消息,从而实现多页签之间的通信。这对于需要在多个标签页之间共享状态、同步数据或实现协作等场景非常有用。请注意,Broadcast Channel API 只在同一域名下的标签页之间有效,不支持跨域通信。
SharedWorker 实现多页签之间通信
SharedWorker 是 HTML5 提供的一种多页签之间共享的 Web Worker。通过 SharedWorker,多个浏览器标签页可以共享一个后台线程,实现跨页面的通信和数据共享。
下面是一个使用 SharedWorker 实现多页签之间通信的示例:
在一个 JavaScript 文件(worker.js)中创建 SharedWorker:
// worker.js
// 在共享 Worker 中监听消息
self.onconnect = function (event) {
const port = event.ports[0]
// 接收消息
port.onmessage = function (event) {
const message = event.data
// 处理消息
// ...
// 发送消息
port.postMessage('Response from SharedWorker')
}
// 断开连接时的处理
port.onclose = function () {
// ...
}
}
在多个页面中分别引入 SharedWorker,并进行通信:
// 页面1
const sharedWorker = new SharedWorker('worker.js')
// 获取共享 Worker 的端口
const port = sharedWorker.port
// 发送消息
port.postMessage('Message from Page 1')
// 接收消息
port.onmessage = function (event) {
const message = event.data
// 处理接收到的消息
// ...
}
// 页面2
// var sharedWorker = new SharedWorker('worker.js')
// // 获取共享 Worker 的端口
// var port = sharedWorker.port
// 发送消息
port.postMessage('Message from Page 2')
// 接收消息
port.onmessage = function (event) {
const message = event.data
// 处理接收到的消息
// ...
}
以上示例中,worker.js
创建了一个 SharedWorker,它会监听来自多个页面的连接请求,并为每个连接创建一个端口(port)。每个页面通过创建 SharedWorker 实例,并通过获取端口对象进行消息的发送和接收。
通过 SharedWorker,页面1和页面2可以实现跨页签的通信。它们可以向共享 Worker 发送消息,并监听共享 Worker 返回的消息,从而实现跨页面的数据交互和共享。
需要注意的是,SharedWorker 需要在支持 SharedWorker 的浏览器中运行,而且需要在服务器环境下运行,即通过 HTTP 或 HTTPS 协议访问页面才能正常工作。
Window.postMessage 使用示例
Window.postMessage()
是 HTML5 提供的一种在不同窗口之间进行跨域通信的方法。它可以安全地向其他窗口发送消息,并在接收方窗口触发消息事件。
下面是一个使用 postMessage()
进行跨窗口通信的示例:
在发送消息的窗口中:
// 发送消息到目标窗口
window.postMessage('Hello, World!', 'https://example.com')
在接收消息的窗口中:
// 监听消息事件
window.addEventListener('message', function (event) {
// 确保消息来自指定域名
if (event.origin === 'https://example.com') {
const message = event.data
// 处理接收到的消息
console.log('Received message:', message)
}
})
在发送消息的窗口中,使用 window.postMessage()
发送消息,第一个参数是要发送的消息内容,第二个参数是目标窗口的源(origin),可以是 URL、域名或通配符 '*'。
在接收消息的窗口中,通过监听 message
事件,可以捕获来自其他窗口的消息。在事件处理程序中,通过 event.origin
可以判断消息来自哪个域名。可以根据需要进行安全性检查,确保只接收来自指定域名的消息。
需要注意的是,postMessage()
通常用于跨窗口通信,可以在不同窗口或不同域名之间进行通信。在使用时需要确保目标窗口的源是可信任的,以防止安全漏洞。同时,接收消息的窗口需要显式地监听消息事件,并进行相应的处理。
跨页面通信方式?
答案
方式 | 适用场景 | 特点 | 典型用法 |
---|---|---|---|
URL参数 | 简单数据传递 | 无需存储,易实现 | 页面跳转时拼接查询参数 |
localStorage/sessionStorage | 同源页面间共享 | 容量大,持久/会话级 | 存取结构化数据,刷新/新开页可用 |
Cookies | 同源页面间共享 | 小容量,支持跨域(配置) | 存储小型数据,兼容性好 |
postMessage API | 窗口/iframe间通信 | 安全、支持跨域 | 父子窗口、iframe消息传递 |
Broadcast Channel API | 多标签页/窗口通信 | 广播机制,简单高效 | 同源多页面实时同步 |
Shared Worker | 多页面共享数据 | 可维护全局状态,支持通信 | 多页面间共享Worker实例 |
WebSocket | 实时通信 | 支持服务端推送,跨页面需管理连接 | 多页面共享同一WebSocket连接 |
延伸阅读
- MDN: Web Storage API - localStorage/sessionStorage 说明
- MDN: postMessage - 跨窗口通信
- MDN: Broadcast Channel API - 多页面广播
- MDN: SharedWorker - 多页面共享Worker
- MDN: WebSocket - 实时通信
History API ?
答案
核心概念
History API 是 HTML5 提供的浏览器历史记录管理接口,允许开发者通过 JavaScript 动态修改 URL 和历史记录,而无需页面刷新。常用方法包括 pushState
、replaceState
、go
、back
、forward
,配合 popstate
事件实现前端路由。
详细解释
history.pushState(state, title, url)
:添加一条历史记录并修改 URL,不刷新页面。history.replaceState(state, title, url)
:替换当前历史记录。history.go(n)
、back()
、forward()
:前进/后退历史记录。window.onpopstate
:监听历史记录变化,常用于响应前进/后退操作。
前端路由利用 History API 实现无刷新页面切换,仅更新部分内容,提升用户体验和性能。切换时可保留页面状态,便于返回时恢复。
代码示例
// 添加历史记录并切换路由
history.pushState({ page: 1 }, '', '/page1')
// 替换当前历史记录
history.replaceState({ page: 2 }, '', '/page2')
// 监听路由变化
window.addEventListener('popstate', e => {
// 根据 e.state 和 location.pathname 渲染页面
})
常见误区
- 仅修改 URL 不会自动渲染页面内容,需手动处理视图更新。
- 直接访问新 URL 或刷新页面时,需服务器返回入口文件(如 index.html),否则会 404。
pushState
/replaceState
不会触发popstate
,需主动调用渲染逻辑。
使用 History API 做前端路由时,务必配置服务器 fallback 规则,避免刷新页面 404。
延伸阅读
原生 js 如何进行监听路由的变化
答案
原生 JS 监听路由变化主要依赖 window
对象的 popstate
事件。该事件会在浏览器历史记录发生变化时触发,包括用户点击前进/后退按钮、或通过 history.pushState()
、history.replaceState()
修改 URL 时。通过监听此事件,可以实现前端路由的页面切换和视图更新。
详细解释
popstate
事件不会在页面首次加载时触发,仅在历史记录切换时触发。- 通过
history.pushState(state, title, url)
或history.replaceState(state, title, url)
可手动变更 URL 并添加/替换历史记录,但不会自动触发popstate
,需手动调用处理逻辑。 event.state
或history.state
可获取当前历史记录的状态对象,便于在路由切换时传递和获取数据。
代码示例
// 路由变化监听
function onRouteChange (event) {
console.log('路由发生了变化', history.state)
// 这里可根据新 URL 渲染页面内容
}
window.addEventListener('popstate', onRouteChange)
// 主动变更路由
function goTo (path, state = {}) {
history.pushState(state, '', path)
// 手动触发处理逻辑
onRouteChange()
}
// 初始化时执行一次
onRouteChange()
常见误区
- 仅通过
pushState
/replaceState
修改 URL 不会自动触发popstate
,需手动处理视图更新。 popstate
只响应历史记录切换,不响应 hash 变化(hash 变化用hashchange
事件)。- 需确保服务器配置支持前端路由,避免刷新页面 404。
单页应用(SPA)推荐统一封装路由切换和监听逻辑,保证页面状态与 URL 同步。
延伸阅读
escape、encodeURI、encodeURIComponent 区别
答案
函数 | 主要用途 | 编码范围 | 空格编码 | 是否编码保留字符 | 是否推荐 | 典型场景 |
---|---|---|---|---|---|---|
escape | 编码字符串 | ASCII外及部分特殊字符 | + | 否 | 否(已废弃) | 早期通用编码(不建议用) |
encodeURI | 编码完整URL | 除保留字符外的所有字符 | 不编码 | 否 | 是 | 整个URL编码 |
encodeURIComponent | 编码URL片段/参数值 | 所有非字母数字字符 | %20 | 是 | 是 | URL参数、片段编码 |
补充说明
- escape 已废弃,实际开发应优先用 encodeURI/encodeURIComponent。
- encodeURI 适合编码整个 URL,不会破坏如
: / ? #
等分隔符。 - encodeURIComponent 适合编码 URL 参数、片段,能确保所有特殊字符被安全转义。
URL参数建议用 encodeURIComponent,避免解析出错。
延伸阅读
getComputedStyle用法?
答案
核心概念
getComputedStyle
是浏览器 BOM(浏览器对象模型)中 window
对象的方法,用于获取元素应用后的最终样式(即所有 CSS 规则和继承、计算后的结果),返回一个只读的 CSSStyleDeclaration
对象。
详细解释
- 语法:
window.getComputedStyle(element, pseudoElement?)
element
:要获取样式的 DOM 元素。pseudoElement
:可选,伪元素字符串(如::before
),通常为null
。
- 返回值:包含所有计算后样式的对象,属性名为驼峰式(如
backgroundColor
)。 - 常见用途:动态获取元素的实际样式(如宽高、颜色),适用于内联样式、外部样式、继承样式等。
代码示例
// 获取元素的实际颜色
const el = document.getElementById('demo')
const style = window.getComputedStyle(el)
console.log(style.color) // 输出如 rgb(0, 0, 0)
// 获取伪元素样式
const beforeStyle = window.getComputedStyle(el, '::before')
console.log(beforeStyle.content)
常见误区
- 不能直接修改
getComputedStyle
返回的对象,只读。 - 获取的值均为计算后值,单位通常为像素(如
width: "100px"
),部分属性可能返回关键字(如auto
)。 - 与
element.style
区别:element.style
只读内联样式,getComputedStyle
读所有最终样式。
延伸阅读
- MDN: getComputedStyle - 官方文档与示例
- CSSStyleDeclaration - 计算样式对象说明
介绍一下 URLSearchParams API
答案
URLSearchParams 是 HTML5 提供的一个 API,用于处理 URL 查询参数。它提供了简单易用的方法来获取、设置、删除和遍历 URL 中的查询字符串参数,避免手动解析字符串的繁琐。
方法 | 作用 | 示例代码 | 说明 |
---|---|---|---|
get | 获取参数值 | urlParams.get('key') | 无则返回null |
set | 设置/更新参数值 | urlParams.set('key','v') | 无则新增 |
append | 追加参数值 | urlParams.append('key','v2') | 同名参数可多值 |
delete | 删除参数 | urlParams.delete('key') | |
has | 判断参数是否存在 | urlParams.has('key') | 返回布尔值 |
forEach | 遍历所有参数 | urlParams.forEach((v,k)=>{...}) |
补充说明
- 支持从字符串或
window.location.search
创建,便于解析和构建查询参数。 - 可与
URL
对象结合动态操作 URL 查询参数,适合 SPA 场景。 - 仅支持字符串类型,复杂对象需手动序列化。
const params = new URLSearchParams('a=1&b=2')
params.set('c', '3')
console.log(params.toString()) // a=1&b=2&c=3
推荐用 URLSearchParams
替代手动拼接/解析查询字符串,提升代码可读性和健壮性。
延伸阅读
requestIdleCallback api
requestIdleCallback
是一个 Web API,它允许开发者请求浏览器在主线程空闲时执行一些低优先级的后台任务,这对于执行如分析、整理状态和数据等不紧急的任务是理想的。这种方法可以提高用户的响应性和页面的整体性能。
以下是 requestIdleCallback
API 的一些关键特点:
何时使用 requestIdleCallback
requestIdleCallback
特别适合那些不直接关联用户交互及响应的任务,这些任务可以延后执行而不会明显影响用户体验。例如:
- 清理工作:如标记的 DOM 节点删除、数据的本地存储同步等。
- 非关键的解析:如解析大量数据。
- 状态更新:如发送不紧急的状态变更。
如何使用 requestIdleCallback
使用 requestIdleCallback
,你需要传递一个回调函数给它,此函数会在浏览器的空闲时间调用。你可以指定一个超时参数,它定义了浏览器在“空闲期”最多可以花费的时间来执行你的回调。
requestIdleCallback(myNonCriticalFunction, { timeout: 5000 })
- myNonCriticalFunction: 这是你想要浏览器在空闲时间执行的函数。
- timeout: 一个可选的参数,表示回调执行时间的上限(以毫秒为单位)。如果超时,浏览器可能在下次空闲机会进行执行。
回调函数参数
你的回调函数会接收到一个 IdleDeadline
对象作为参数,通常命名为 deadline
。这个对象包含两个属性:
- didTimeout - 一个布尔值,如果超时已经被触发为
true
。 - timeRemaining - 返回当前空闲阶段剩余时间的函数,单位是毫秒。
function myNonCriticalFunction (deadline) {
while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && someCondition()) {
// 执行工作直到时间用完或下次更新不是必要的
}
// 如果还有未完成的工作,可以请求下一次空闲周期
if (someCondition()) {
requestIdleCallback(myNonCriticalFunction)
}
}
注意事项
requestIdleCallback
不保证你的回调会在一个特定的时刻被调用,它只在浏览器需要的时候调用。- 执行低优先级任务时,不应该太过频繁或执行时间太长,以免影响页面性能。
- 这个 API 为了最大化性能优化,会强制性地结束你的任务,在不迟于指定的超时时长执行结束。
Cross-Browser Compatibility (跨浏览器兼容性)
你可能需要 polyfills(垫片库)来确保 requestIdleCallback
的兼容性,因为它并不是在所有浏览器中都有原生支持。
使用 requestIdleCallback
,开发者可以更好地利用浏览器的空闲序列来执行不紧急的任务,同时保持用户交互的流畅度。
参考文档
postmessage
postMessage 是 HTML5 提供的一种跨窗口通信机制,可以在不同窗口、甚至不同域名之间进行通信,从而实现跨域通信。通过在一个窗口中调用 postMessage 方法向另一个窗口发送消息,接收窗口可以监听 message 事件,以接收发送过来的消息。
使用 postMessage 可以解决一些跨域问题,如在一个域名下的网页与其他域名下的网页进行通信。具体来说,当两个窗口的协议、主机名或端口不同时,就会发生跨域问题。但使用 postMessage 可以突破同源策略的限制,实现不同域之间的通信。
一般情况下,为了保证安全,使用 postMessage 进行跨域通信时,需要在目标窗口中设置 window.postMessage 的接收地址,只有来自该地址的消息才能被接收,从而避免了安全问题。同时,可以使用 origin 参数来限制消息来源,避免恶意攻击。
码举例
假设我们有两个域名为 资料 和 资料 的网站,现在需要在这两个网站之间进行跨域通信。
在 资料 的页面中,我们可以使用以下代码向 资料 发送一条消息:
const targetOrigin = 'https://domain-b.com'
const message = 'Hello, domain-b!'
window.parent.postMessage(message, targetOrigin)
这里的 window.parent 表示当前页面所在窗口的父级窗口,即指向 资料 的窗口对象。
在 资料 的页面中,我们可以使用以下代码监听消息并做出相应处理:
window.addEventListener('message', event => {
const origin = event.origin // 发送消息的域名
const data = event.data // 消息内容
if (origin === 'https://domain-a.com') {
console.log('Received message from domain-a:', data)
}
})
使用 postMessage 进行跨域通信需要注意安全问题,特别是在确定目标域名时应该使用固定的字符串而不是动态生成的值,以避免被攻击者利用。
frame 是否可以使用 postMessage 通信?
不同的 iframe 和同一个页面之间也可以通过 postMessage 方法进行通信。这种情况下,通信的流程和同一页面中不同窗口的通信流程基本相同。只不过发送方和接收方不在同一页面中,而是在不同的 iframe 中。假设页面 A 中有两个 iframe,一个是 B 页面,另一个是 C 页面,现在需要在这两个 iframe 之间进行通信,具体的实现过程如下:
在 B 页面的脚本中使用 postMessage 方法向父级页面 A 发送消息:
window.parent.postMessage('message from B', 'http://localhost:3000')
在 C 页面的脚本中使用 postMessage 方法向父级页面 A 发送消息:
window.parent.postMessage('message from C', 'http://localhost:3000')
在页面 A 的脚本中监听 message 事件,接收来自不同 iframe 的消息:
window.addEventListener('message', function (event) {
// 判断消息来源是否是指定的 iframe
if (event.origin === 'http://localhost:3000') {
console.log('Received message: ' + event.data)
}
})
需要注意的是,在这个过程中,B 和 C 两个 iframe 都需要和父级页面 A 都处于同一域名下,否则会触发跨域安全限制。