前端高频面试题-精选篇-浏览器模块
# 1 跨标签页通讯
30 秒速记
- 同源首选
BroadcastChannel:语义直接,同频道名的所有同源上下文都能收到(发送方自己不触发) storage事件兼容性最广,但要借localStorage传消息;两个坑:只通知其他页面、同值不触发(要带时间戳)SharedWorker适合"多个标签页共用一条WebSocket连接"这类需要共享状态的场景- 需要跨设备/跨浏览器时才用服务端通道(
WebSocket/SSE)——纯前端方案的天花板是"同一浏览器内的同源页面" cookie+setInterval轮询是过时写法:有延迟、持续耗CPU、cookie还跟着每个请求上传
同源标签页之间我一般优先用 BroadcastChannel,需要跨设备时才考虑服务端的 WebSocket 或 SSE。 BroadcastChannel 语义直接,但发送方自身不会收到消息;兼容旧环境可用 storage 事件兜底,并给消息加时间戳避免同值不触发。多个标签页需要共享状态或复用一条长连接时,SharedWorker 更合适,但调试和兼容性需要取舍。有父子窗口关系也能用 postMessage,同时必须校验 origin。
跨标签页通信要先分两类场景:同源的纯浏览器通信,和需要服务端中转的跨设备同步。
① BroadcastChannel(同源首选)
const channel = new BroadcastChannel('auth')
channel.postMessage({ type: 'logout' }) // A 标签页发
channel.onmessage = (e) => { ... } // B 标签页收(发送方自己不触发)
window.addEventListener('pagehide', () => channel.close())
语义最直接:同一个频道名的所有同源上下文都能收到。
② storage 事件(兼容性最广)
localStorage.setItem('msg', JSON.stringify({ type: 'logout', t: Date.now() }))
window.addEventListener('storage', (e) => { if (e.key === 'msg') { /* ... */ } })
两个坑:只通知其他标签页(自己不触发)、同值不触发(连发两次一样的内容第二次收不到,所以要带时间戳)。它本质是"用存储的副作用做通信",能用 BroadcastChannel 就别用它。
③ SharedWorker(需要共享状态时)
多个标签页连到同一个 Worker 实例,适合"多个页面共用一条 WebSocket 连接"——避免开十个标签就建十条连接。代价是调试麻烦、Safari 支持长期缺失。
④ window.opener + postMessage
只适用于有父子关系的窗口(window.open 打开的),且必须严格校验 e.origin,否则任何页面都能给你发消息。
⑤ 服务端通道(WebSocket/SSE)
只有需要跨设备、跨浏览器同步时才用。同一台机器的标签页之间用它属于杀鸡用牛刀,还多了一份服务端压力。
已过时的写法:cookie + setInterval 轮询 —— 有延迟、持续消耗 CPU、cookie 还会跟着每个请求上传浪费带宽。老资料里常见但现在没有理由再用。
选型速查:同源且只是通知 → BroadcastChannel;要兼容很老的浏览器 → storage;要共享一条长连接 → SharedWorker;跨设备 → 服务端推送。
下面是更多说明:
不同标签页间的通讯,本质原理就是去运用一些可以 共享的中间介质,因此比较常用的有以下方法:
- 通过父页面
window.open()和子页面postMessage- 异步下,通过
window.open('about: blank')和tab.location.href = '*'
- 异步下,通过
- 设置同域下共享的
localStorage与监听window.onstorage- 重复写入相同的值无法触发
- 会受到浏览器隐身模式等的限制
- 设置共享
cookie与不断轮询脏检查(setInterval) - 借助服务端或者中间层实现
面试官追问
追问 1订单后台的 A、B 两个同源标签页都监听 storage,A 连续两次写入完全相同的 {type: "refresh"},为什么 B 只收到第一次,而 A 一次也收不到?
storage 事件只通知其他同源页面,执行 localStorage.setItem 的当前标签页不会收到;重复写入相同值也不会再次触发。消息中加入时间戳或唯一标识可确保值变化,A 页若也要执行逻辑,应在发送时直接调用本地处理函数。
追问 2登录系统要求 A 页退出后,另外十个同源标签页立即清理会话;不考虑老旧浏览器时,你会怎样封装发送、接收和销毁?
创建同名的 BroadcastChannel('auth'),A 页用 postMessage({ type: 'logout' }) 广播,其他页面在 onmessage 中清理状态并跳转。页面离开时在 pagehide 关闭频道;发送方不会收到自己的消息,因此 A 页仍需同步执行本地退出流程。
追问 3产品把“多标签页同步”扩大为普通窗口、无痕窗口、另一台手机都要同步登录状态;继续用 BroadcastChannel 为什么无法满足?
BroadcastChannel 只覆盖同源且处于可共享浏览器环境的上下文,不能承担跨浏览器、跨隔离配置或跨设备传递。需求跨过浏览器边界后,应由服务端通过 WebSocket 或 SSE 中转;这会增加连接管理、鉴权和服务端资源成本。
追问 4线上反馈偶尔收不到跨页消息,系统使用 localStorage 作为消息总线;你会先从哪些可复现条件排查,而不是直接换协议?
先确认两个页面是否同源、写入值是否与旧值完全相同、监听器是否在写入前注册,并验证用户是否处于隐身等受限环境。再检查业务是否误以为发送页也会触发 storage;若这些语义无法满足可靠性要求,应增加服务端状态校验,而不能把存储事件当可靠消息队列。
追问 5行情站打开八个标签页后会建立八条 WebSocket,后端要求减少连接;团队在 BroadcastChannel 和 SharedWorker 之间争论,你会怎么定?
如果只是标签页间通知,BroadcastChannel 更直接;若目标是让多个页面共享同一个有状态的 WebSocket 实例,SharedWorker 更贴合,因为各页可连接同一个 Worker。代价是调试复杂且浏览器支持存在边界,无法接受时只能保留多连接或改由服务端协调。
# 2 浏览器架构
30 秒速记
Chrome是多进程架构:浏览器主进程、渲染进程(每个标签页/站点一个)、GPU进程、网络进程、插件进程- 多进程换来的是稳定性(一个标签页崩了不影响整体)和安全性(渲染进程被沙箱关着,拿不到系统资源)
- 代价是内存开销大 —— 所以
Chrome有进程合并策略:同一站点的多个标签页会共用渲染进程 - 开启站点隔离后,跨站的
iframe会被放进独立进程 —— 这也是跨站iframe无法同步通信的根本原因 - ⚠️ 渲染进程内部才是我们最该关心的:主线程(
JS+ 样式 + 布局 + 绘制)、合成线程、光栅化线程
Chrome 采用多进程架构,核心包括浏览器主进程、渲染进程、网络进程和 GPU 进程。 这样一个标签页崩溃通常不会拖垮整个浏览器,渲染进程也能通过沙箱限制系统资源访问,代价是占用更多内存。开启站点隔离后,跨站 iframe 会进入独立进程,只能通过 postMessage 等方式通信。前端更该关注渲染进程内部:JS、样式、布局和绘制共用主线程,长任务会直接卡住页面。
Chrome 是多进程架构,主要有五类进程:
| 进程 | 职责 |
|---|---|
| 浏览器主进程 | 界面显示、用户交互、子进程管理、文件存储 |
| 渲染进程 | 把 HTML/CSS/JS 变成可交互的页面 —— 每个标签页/站点一个 |
| 网络进程 | 所有网络资源加载 |
| GPU 进程 | 图形绘制、合成、三维渲染 |
| 插件进程 | 每个插件独立,崩溃不影响页面 |
为什么要多进程?
- 稳定性 —— 一个标签页崩了不会带走整个浏览器
- 安全性 —— 渲染进程被沙箱关着,拿不到系统资源;恶意页面无法直接读写文件
- 代价是内存开销大,所以
Chrome有进程合并策略:同一站点的多个标签页会共用渲染进程
站点隔离(Site Isolation):开启后跨站的 iframe 会被放进独立进程 —— 这也是跨站 iframe 无法同步通信、只能 postMessage 的根本原因。
但前端最该关心的是渲染进程内部:
渲染进程
├── 主线程 ← JS 执行 + 样式计算 + 布局 + 绘制(都在这一条线上)
├── 合成线程 ← 处理 transform/opacity、滚动
├── 光栅化线程池 ← 把绘制指令变成位图
└── Worker 线程 ← Web Worker
"JS 是单线程"说的正是这条主线程 —— JS 执行、样式计算、布局、绘制全在上面排队,所以一段长任务能卡住整个页面。
理解这个划分,很多优化建议就不用死记了:
- 为什么动画只推荐
transform/opacity?因为它们能在合成线程上跑,主线程忙也不影响 - 为什么
Web Worker不能操作DOM?因为DOM属于主线程,多线程同时改会有竞态 - 为什么重计算要搬进
Worker?因为它在独立线程,不占主线程
多进程对前端的三个实际影响:同站标签页共用渲染进程(一个卡全都卡)、跨站 iframe 无法同步通信、任务管理器能按进程定位内存异常。
下面是更多说明:
- 用户界面
- 主进程
- 内核
- 渲染引擎
JS引擎- 执行栈
- 事件触发线程
- 消息队列
- 微任务
- 宏任务
- 消息队列
- 网络异步线程
- 定时器线程
