面试题[浏览器]
1. 浏览器基础
1.1 常见浏览器所用内核
目前主流的浏览器内核主要有三个:Google主导的 Blink、Mozilla的 Gecko,以及苹果的 WebKit。它们共同构成了现今浏览器技术的核心。
主流桌面浏览器及其内核:
| 浏览器 | 内核 | 说明 |
|---|---|---|
| Google Chrome | Blink | 由 Google 基于 WebKit 开发,是目前全球使用最广泛的浏览器引擎。 |
| Microsoft Edge | Blink | 微软已全面转向 Blink,弃用自研的 EdgeHTML 内核,性能显著提升,兼容 Chrome 插件。 |
| Mozilla Firefox | Gecko | 开源且注重标准、隐私与安全,是除 Blink 外最主流的独立内核之一。 |
| Apple Safari | WebKit | 苹果所有设备的默认浏览器,对苹果生态有深度优化和极佳的性能稳定性。 |
| Opera | Blink | 早期曾采用自研的 Presto 内核(已停用),现已全面转向 Blink 内核。 |
| Brave | Blink | 主打隐私保护的浏览器,基于 Blink 内核开发。 |
| Vivaldi | Blink | 主打高定制性,同样基于 Blink 内核。 |
内核演进趋势总结:
- 技术演进:浏览器内核从早期的 Trident(IE)、Presto(Opera 旧版)等百花齐放,经过激烈竞争,最终收敛到由 Blink、Gecko、WebKit 三足鼎立的局面。
- Chromium 主导:Blink(Chromium) 内核凭借强大的性能、活跃的开源社区和标准支持,已占据绝对主导地位,市场份额超过 80%,Edge、Opera、Vivaldi 等均已成为其生态一员。
- 弱肉强食的市场:浏览器内核已成为门槛极高的领域,需要巨大的持续投入。Edge 放弃自研 EdgeHTML 转投 Chromium,正是这一趋势的体现。
1.2 浏览器渲染页面的过程
浏览器渲染页面的过程是一个复杂而精细的任务,它涉及多个步骤和组件的协同工作。以下是浏览器渲染页面的主要过程:
- HTML解析与DOM树构建:
- 浏览器从网络获取HTML文件,并从上到下逐行解析。
- 在解析过程中,浏览器会构建文档对象模型(DOM),这是一个表示HTML文档结构的树形数据结构。DOM的节点与HTML文档中的标签、文本节点和属性相对应。
- CSS解析与CSSOM树构建:
- 与HTML解析并行,浏览器还会解析外部和内部的CSS样式表。
- CSS解析后,会生成CSS对象模型(CSSOM),这也是一个树形结构,描述了页面上所有的CSS选择器及其层级结构和属性。
- 渲染树生成:
- 一旦DOM和CSSOM都构建完成,浏览器会结合这两个树生成渲染树(Render Tree)。
- 渲染树只包含需要显示的节点和样式信息,例如,设置了
display: none的节点不会出现在渲染树中。
- 布局处理:
- 渲染树生成后,浏览器会进行布局处理,也称为回流(Reflow)。
- 布局处理阶段,浏览器会根据CSS样式信息为渲染树中的每个节点计算其位置和尺寸。这涉及到复杂的盒模型计算,以确保元素在页面上正确排列。
- 绘制:
- 在布局处理完成后,浏览器会进入绘制阶段。
- 绘制阶段,浏览器会遍历渲染树,将每个节点的内容绘制到屏幕上。这是一个渐进式的过程,边解析边绘制,以提高用户体验。
- JavaScript执行:
- 在HTML解析过程中,如果遇到
<script>标签,浏览器会暂停HTML解析,等待JavaScript文件下载并执行。 - JavaScript代码可能会修改DOM或CSSOM,从而导致回流和重绘。因此,JavaScript的执行顺序和性能对页面渲染有很大影响。
- 需要注意的是,使用
defer或async属性可以改变JavaScript的加载和执行方式,以优化页面性能。
- 在HTML解析过程中,如果遇到
- 分层与合成(可选):
- 对于复杂的页面,浏览器可能会将页面分成多个图层(Layer),以提高渲染效率。
- 每个图层都会单独生成绘制指令,并交给合成器线程进行合成和栅格化。
- 合成器线程会将图层合并成一个完整的图像,然后显示在屏幕上。
此外,在渲染过程中,浏览器还会进行性能优化,如懒加载、按需加载、资源压缩等,以提高页面加载速度和用户体验。
1.3 现代浏览器多进程
现代浏览器采用多进程架构,主要包括以下几个常见的进程:
- 浏览器主进程(Browser Process):也称为浏览器引擎进程或浏览器内核进程,负责协调和管理整个浏览器的工作。它处理用户界面、显示和用户交互,还管理其他子进程。
- 渲染进程(Renderer Process):每个标签页或窗口都会有一个独立的渲染进程,负责处理网页内容的渲染。渲染进程使用浏览器的渲染引擎来解析 HTML、CSS,构建渲染树并进行页面布局和绘制。
- 插件进程(Plugin Process):用于运行浏览器插件,如Flash Player、PDF 阅读器等。为了增强安全性和稳定性,现代浏览器已经逐渐停止对插件的支持。
- GPU 进程(GPU Process):处理图形相关的任务,如页面的绘制和图形加速。它负责使用计算机的 GPU(图形处理器)来执行图形操作,提高页面渲染的性能。
- 网络进程(Network Process):处理网络请求和响应,负责发送和接收网络数据。它负责与服务器通信,下载网页内容、样式、脚本以及其他资源。
- JavaScript 引擎进程(JavaScript Engine Process):负责解析和执行 JavaScript 代码。现代浏览器通常使用高性能的 JavaScript 引擎,如V8引擎,它在单独的进程中运行。
这些进程通过进程间通信(IPC)进行通信和协调,以实现浏览器的各种功能。多进程架构的设计可以提高浏览器的安全性、稳定性和性能,同时隔离不同的任务,避免一个标签页或插件的崩溃影响整个浏览器的运行。
1.4 浏览器渲染进程
渲染进程是浏览器中负责处理网页渲染的核心组件之一。它负责将 HTML、CSS 和 JavaScript 转换为可视化的网页内容,以供用户在浏览器中查看和与之交互。
以下是渲染进程的主要功能和工作流程:
- 解析 HTML:渲染进程接收到网络传输的 HTML 文档后,通过解析器逐行读取并解析文档结构。解析器将 HTML 标记转换为解析树(DOM 树)。
- 构建渲染树:基于 DOM 树和 CSS 样式信息,渲染进程构建渲染树(Render Tree)。渲染树由可视化的网页内容组成,其中每个节点都与其对应的样式信息关联。
- 布局(Layout):渲染树中的每个节点都包含了位置和尺寸信息。布局引擎根据这些信息,计算出每个节点在屏幕上的精确位置,并生成布局(Layout)。
- 绘制(Painting):渲染进程使用绘图引擎将布局转换为可视化的像素信息,即绘制。绘制涉及颜色、字体、图像等元素的渲染。
- 合成(Compositing):渲染进程将绘制的结果合成为最终的页面图像。这涉及将多个图层(Layer)合并,并按正确的顺序进行显示。
- 显示和交互:最终,渲染进程将渲染好的页面图像显示在浏览器窗口上,用户可以与之交互。渲染进程还负责处理用户的输入事件,并将其传递给相应的处理程序。
需要注意的是,现代浏览器通常采用多进程架构,其中渲染进程是其中之一。其他进程如网络进程、JavaScript 引擎进程和浏览器主进程等也扮演着不同的角色,协同工作来实现浏览器的功能。渲染进程与这些进程之间通过进程间通信(IPC)来进行数据交换和协调。
1.5 DOM 树是怎么生成的
DOM 树(Document Object Model Tree)的生成过程,本质上是浏览器将 HTML 文本 解析为 树状结构 的过程。这个解析过程由浏览器的 HTML 解析器 负责,它会逐字节读取 HTML,并遵循一套标准化的算法(参考 HTML 规范中的 标记化 和 树构建 环节)。整体流程图:
原始 HTML 字节流 → 解码 → 字符流 → 词法分析 → 令牌流 → 语法分析(树构建) → DOM 树
↑ ↓
编码元信息 脚本执行(可能阻断)2. 浏览器进阶
2.1 跨域问题
什么是跨域
跨域指的是浏览器请求一个不同源的资源。而“源”(Origin)由三部分组成:
- 协议(如
http://或https://) - 域名(如
example.com) - 端口(如
80、443、8080,当端口与协议默认端口不同时)
只有当协议、域名、端口三者完全一致时,才叫同源;只要有一个不同,就是跨域。
注意:
localhost和127.0.0.1虽然指向同一台机器,但浏览器认为它们是不同的域名,因此也是跨域。- 协议(如
为什么浏览器要限制跨域
这是为了保障用户安全,核心机制叫做同源策略(Same-Origin Policy)。如果没有同源策略,恶意网站可以随意读取你在其他网站(如网银、邮箱)上的数据。例如:
- 你登录了银行网站
bank.com(Cookie 存储在浏览器中)。 - 然后你访问了一个恶意网站
evil.com。 evil.com的脚本可以发起请求到bank.com/api/account,由于浏览器会自动带上bank.com的 Cookie,恶意网站就能获取你的账户信息。
同源策略 阻止这种操作:只有同源的脚本才能读取另一个源的响应内容。浏览器会拦截跨域请求的响应,不允许当前页面的 JavaScript 读取,但请求本身有时还是会发出(取决于请求类型)。这样就隔离了不同源的数据。
注意:跨域限制是浏览器施加的,不是服务器。如果你用 Postman、curl 等工具直接请求接口,不存在跨域问题。
- 你登录了银行网站
解决跨域的常见方法
CORS(跨域资源共享)—— 最正统、最常用:CORS(Cross-Origin Resource Sharing)是通过服务器在响应头中添加特定字段,告诉浏览器允许跨域访问。
关键响应头:
Access-Control-Allow-Origin:指定允许的源(*表示任意源,但不能与凭证头一起用)Access-Control-Allow-Methods:允许的 HTTP 方法(如 GET, POST)Access-Control-Allow-Headers:允许的自定义请求头Access-Control-Allow-Credentials:是否允许携带 Cookie(设为true时需要指定具体源,不能用*)Access-Control-Max-Age:预检请求的缓存时间
JSONP(JSON with Padding)—— 仅限 GET 请求,老项目中常见:利用
<script>标签不受跨域限制,让服务器返回一段 JavaScript 代码(调用前端预定义的回调函数)。代理(Proxy)—— 前端开发中最常用的临时方案:在同源的中间层转发请求,绕过浏览器限制。
- 开发环境代理(CRA、Vite、Webpack devServer)
- Nginx 反向代理
postMessage(跨文档通信):用于
iframe、弹出窗口之间跨域传递消息。WebSocket:WebSocket 本身不受同源策略限制(但 WebSocket 协议有
Origin头部,服务器可以校验)。因此可以通过 WebSocket 实现跨域通信。最佳实践:
- 生产环境:优先采用 CORS 进行精细化配置。
- 开发环境:使用脚手架自带的 proxy 功能。
- 特殊场景(iframe):使用 postMessage。
2.2 浏览器存储
浏览器存储指的是浏览器用于在客户端保存和管理数据的机制。以下是几种常见的浏览器存储方式:
| 存储方式 | 容量(典型) | 生命周期 | 作用域 | 同步/异步 | 数据类型 |
|---|---|---|---|---|---|
| Cookie | 4KB | 可设置过期时间 | 同源,可设置路径域 | 同步 | 字符串 |
| localStorage | 5-10MB | 永久(除非手动清除) | 同源(协议+域名+端口) | 同步 | 字符串(键值对) |
| sessionStorage | 5-10MB | 标签页关闭即消失 | 同源 + 同会话(同一标签页) | 同步 | 字符串(键值对) |
| IndexedDB | 通常 >250MB,甚至 GB 级 | 永久(除非手动清除) | 同源(可跨多个 Tab) | 异步 | 任何结构的数据(对象、二进制、文件等) |
| Cache Storage | 取决于可用磁盘空间(浏览器管理) | 永久(除非清除或 LRU 淘汰) | 同源(Service Worker 控制) | 异步 | Response 对象 (HTTP 请求/响应对) |
| File System Access API | 取决于用户授权 | 永久(用户授权后) | 同源 + 用户授权 | 异步 | 文件和目录 |
各存储方式详解:
- Cookie
- 本质:服务器发送给浏览器的少量数据,浏览器后续请求会自动携带(通过
Cookie请求头)。 - 操作接口:
document.cookie(字符串读写)。 - 容量:每个 Cookie 最大 4KB,每个域名下 Cookie 总数有限(约 20-50 个,取决于浏览器)。
- 生命周期:可设置
Expires或Max-Age,若未设置则为会话 Cookie(关闭浏览器失效)。 - 作用域:由
Domain和Path属性决定;默认仅为当前域名及路径,但不包括子域(除非显式设置 Domain)。 - 安全性:可设置
HttpOnly(禁止 JS 访问,防 XSS)、Secure(仅 HTTPS 传输)、SameSite(防 CSRF)。 - 使用场景:
- 身份认证凭证(Session ID)
- 少量用户偏好
- 跟踪用户行为(但受隐私政策限制)
- 本质:服务器发送给浏览器的少量数据,浏览器后续请求会自动携带(通过
- Web Storage(localStorage 和 sessionStorage)
- 共同特点:
- 键值对存储,键和值都必须是字符串(非字符串会被转为字符串,存对象需
JSON.stringify())。 - 提供同步 API:
setItem(key, value)、getItem(key)、removeItem(key)、clear()。 - 可监听
storage事件(同源下其他标签页修改存储可收到通知,但修改自身页面不触发)。
- 键值对存储,键和值都必须是字符串(非字符串会被转为字符串,存对象需
- localStorage:
- 永久存储,除非用户清除网站数据或调用
clear()。 - 作用域:同源(协议、域名、端口都相同)的所有标签页/窗口共享。
- 永久存储,除非用户清除网站数据或调用
- sessionStorage:
- 生命周期为页面会话:关闭标签页或浏览器则消失(页面重载或恢复不会丢失,但打开新标签页会创建新会话,不共享)。
- 作用域:同源且同一浏览上下文组(即同一个标签页中多个 iframe 也算同一会话;但同源的不同标签页之间不共享)。
- 容量:通常 5MB~10MB(不同浏览器差异)。
- 共同特点:
- IndexedDB
- 本质:一个事务型、对象仓库式的 NoSQL 数据库,运行在浏览器中。
- 容量:通常远大于 localStorage(桌面端可达几百 MB 甚至 GB 级别,具体取决于磁盘空间)。
- API:异步、基于事件回调或 Promise(现代封装如
idb库使之更易用)。 - 特点:
- 支持索引、游标、事务(ACID 部分保证)。
- 可存储大量结构化数据,包括 Blob(二进制大对象)、ArrayBuffer、File 等。
- 支持版本升级 migration。
- 同源策略限制。
- Cache Storage
- 本质:Service Worker 规范的一部分,用于缓存网络请求的
Request和Response对。 - API:
caches全局对象,提供open、match、put、delete、keys等异步方法。 - 生命周期:持久化存储,除非用户清除站点数据或浏览器因磁盘压力而驱逐(通常会优先清理不常使用的缓存)。
- 作用域:Service Worker 作用范围内的所有页面共享(但 Service Worker 本身可控制具体哪些请求被缓存)。
- 使用场景:
- PWA:实现离线浏览、缓存静态资源(HTML、CSS、JS、图片)。
- 自定义网络策略(Cache First, Network First 等)。
- 本质:Service Worker 规范的一部分,用于缓存网络请求的
- File System Access API
- 本质:允许 Web 应用直接读写用户本地文件或文件夹(需获得用户授权)。
- API:
window.showOpenFilePicker、showSaveFilePicker、showDirectoryPicker,返回FileSystemFileHandle等对象。 - 容量:不限(受限于用户磁盘空间),每次读写都需要用户明确授权。
- 生命周期:授权后可永久访问,直至用户撤销权限或清除站点数据。
- 使用场景:
- 在线 IDE、图片编辑器、文档编辑器,直接编辑本地文件。
- 需要访问本地目录进行批量处理的应用。
- 兼容性:较新(Chrome 86+,Edge,Opera;Firefox 和 Safari 未支持或部分支持)。
2.3 浏览器缓存
浏览器缓存是前端性能优化中非常重要的一环,它能显著减少网络请求、降低带宽消耗、加快页面加载速度。
浏览器缓存的位置
浏览器缓存可以存储在多个地方,按优先级从高到低:
- 内存缓存(Memory Cache):存储在内存中,速度最快,但容量小,进程结束后释放(如关闭标签页)。通常用于加载页面过程中短暂存在的资源(如解析 HTML 时的 CSS、JS)。
- 硬盘缓存(Disk Cache):存储在磁盘上,容量大,持久化存储。即使关闭浏览器再打开,缓存依然存在。速度比内存慢,但比网络请求快很多。
- Service Worker 缓存:由开发者通过 Service Worker 脚本控制,可以拦截请求并返回自定义缓存。优先级最高,一旦 Service Worker 接管,可以决定是否走网络或使用缓存。
- 推送缓存(Push Cache):HTTP/2 中服务器推送的资源会被缓存在这个特定区域,生命周期较短,只在当前会话有效。
实际请求时,浏览器会按照 Service Worker → 内存缓存 → 硬盘缓存 → 推送缓存 的顺序寻找缓存。如果都没有,再发起网络请求。
缓存策略总览
浏览器缓存主要分为两大策略:强缓存 和 协商缓存。它们的核心区别在于:是否需要向服务器验证缓存是否有效。
策略 判定依据 是否发送请求 谁决定 响应状态码 强缓存 本地缓存未过期 否(直接使用本地) 本地浏览器 200 (from disk/memory cache) 协商缓存 缓存已过期或不确定 是(请求带上验证标识) 服务器 304 Not Modified 或 200
2.4 浏览器存储和浏览器缓存的区别
浏览器存储(Browser Storage):指的是浏览器在客户端(用户的设备)上保存和管理数据的机制。它主要用于在浏览器中存储数据,以供后续的访问和使用。常见的浏览器存储机制包括 Cookie、Web Storage(sessionStorage 和 localStorage)、IndexedDB 等。这些存储机制可用于存储用户首选项、会话数据、缓存数据等。浏览器存储是由开发者主动使用相应的 API 将数据存储在客户端,并通过 API 进行读取和更新。
浏览器缓存(Browser Cache):是指浏览器在本地缓存网页资源,以便在后续的访问中能够更快地加载和呈现页面内容。浏览器缓存的主要目的是减少网络请求,提高网页的加载速度和性能。当浏览器首次请求某个资源时,它会检查是否存在缓存的副本,如果存在且仍然有效,则直接从缓存中获取资源而不是向服务器重新请求。常见的缓存机制包括浏览器缓存、Service Worker 缓存和 HTTP 缓存。浏览器缓存是由浏览器根据服务器返回的响应头中的缓存相关字段来决定资源是否被缓存以及缓存的有效期。
核心区别一句话总结:
- 浏览器存储:存的是你自己定义的数据,你决定什么时候读、写。
- 浏览器缓存:存的是网络请求回来的资源,浏览器根据服务器指示自动决定是否复用。
2.5 浏览器缓存和 HTTP 缓存的区别
浏览器缓存是 HTTP 缓存的一种具体实现(基于 HTTP 缓存机制实现),或者说,HTTP 缓存包含了浏览器缓存(私有缓存)和共享缓存(如代理服务器、CDN),两者通过 Cache-Control 的 public/private 及 s-maxage 等指令协同工作。因此,两者不是并列关系,而是包含关系。
2.6 localStorage、sessionStorage 和 cookie 的区别
| 特性 | localStorage | sessionStorage | cookie |
|---|---|---|---|
| 存储容量 | 约 5–10 MB(不同浏览器有差异) | 约 5–10 MB | 约 4 KB |
| 生命周期 | 永久存储,除非用户手动清除代码删除 | 当前会话(页面标签或浏览器关闭即清除) | 可设置过期时间;未设置时,浏览器关闭即失效 |
| 作用域 | 同源(协议 + 域名 + 端口)内所有标签页共享 | 同源且同一标签页(不同标签页不共享) | 同源,并可设置 path 限制路径 |
| 与服务器的通信 | 不会自动发送到服务器 | 不会自动发送到服务器 | 每次 HTTP 请求都会自动携带(通过 Cookie 请求头),会增加请求体积 |
| 操作 API | setItem/ getItem/ removeItem/ clear | setItem/ getItem/ removeItem/ clear | 需手动解析 document.cookie 字符串(无原生便捷方法) |
| 安全性 | 易受 XSS 攻击,但不会自动发送,可避免 CSRF | 同左 | 可设置 HttpOnly 防止 XSS 读取,但存在 CSRF 风险;Secure 标志可限定 HTTPS 传输 |
3. 浏览器拓展
3.1 JavaScript 的内存管理
JavaScript 的内存管理是自动的,主要依赖垃圾回收器(Garbage Collector, GC) 来回收不再使用的内存。下面讲解其核心机制、常见问题及优化建议。
内存生命周期
无论什么语言,内存生命周期都分为三步:
- 分配:声明变量、函数或创建对象时,JS 引擎自动分配内存。
- 使用:读写变量、调用函数等操作。
- 释放:当内存不再被需要时,垃圾回收器接管并释放它。
垃圾回收(GC)主要算法
- 引用计数(早期、已被主流引擎抛弃)
- 原理:每个对象记录被引用的次数,次数为 0 时释放。
- 致命缺陷:循环引用(如两个对象互相引用)会导致计数永远不会归零,造成内存泄漏。
- 目前仅在老旧 IE(如 IE6-7)的某些 DOM 对象中存在此问题。
- 标记-清除(现代浏览器核心算法)
- 原理:从根(全局对象、当前执行栈的局部变量等)出发,递归标记所有能访问到的对象;未标记的即为不可达对象,直接清除。
- 优点:能正确处理循环引用(因为互相引用的对象若从根不可达,都会被标记为垃圾)。
- 缺点:内存碎片化、可能导致暂停(Stop-the-World),但现代 V8 通过增量标记、并发回收等优化了性能。
- 引用计数(早期、已被主流引擎抛弃)
常见内存泄漏场景及避免方法
即使有 GC,如果代码中无意识地持续持有对象引用,仍会造成内存泄漏。
场景 示例 解决方案 意外全局变量 未声明的变量 function foo() { a = 10 }使用 'use strict';用完后delete或赋null被遗忘的计时器 / 回调 setInterval中引用了大对象,但从未clearInterval在组件销毁或不需要时及时清除 闭包 函数内部引用外部大数组,且返回的函数长期持有该引用 审查闭包作用域,不再需要时解除引用(如将变量赋 null)DOM 引用 缓存了某个 DOM 节点,但该节点被移除后,变量仍引用它 使用弱引用( WeakMap/WeakSet),或手动赋null脱离 DOM 树的节点 一个 DOM 节点被移出文档,但 JS 中仍有变量引用它 同上,或直接删除引用 辅助内存管理的工具与技巧
WeakMap/WeakSet:键名是弱引用,不影响垃圾回收。适用于数据缓存、DOM 关联数据。- Chrome DevTools:
- Performance 面板查看内存时间线;
- Memory 面板拍摄堆快照,比较对象引用与泄漏点。
3.2 WebKit 和 V8 的区别
WebKit 和 V8 是两个在浏览器领域至关重要但职责完全不同的项目。简单来说,WebKit 是一个浏览器的“总装车间”,负责处理页面的渲染和加载;而 V8 是其中的一个“核心零部件”,或者说一个高性能的“引擎”,专门负责执行 JavaScript 代码。
| 维度 | 🏢 WebKit | 🚀 V8 |
|---|---|---|
| 项目性质 | 一个浏览器引擎(或称渲染引擎),是个庞大的“操作系统”。 | 一个 JavaScript 执行引擎,是个专门的“虚拟机”。 |
| 核心职能 | 将 HTML、CSS 转换为用户能看到的网页画面,并管理整个网页的生命周期。 | 读取、编译并执行网页中的 JavaScript 代码。 |
| 关键组件 | - WebCore:进行页面排版、计算布局的核心。 - JavaScriptCore:可选的 JS 引擎,能换成 V8 等。 - Ports:供不同平台集成的移植接口。 | - Ignition:基础解释器,快速启动代码。 - TurboFan:优化编译器,提升代码性能。 - Orinoco:内存管理者,负责垃圾回收。 |
| 所属/用户 | 苹果(Apple)主导并维护的开源项目。 | Google 主导并维护的开源项目。 |
| 典型应用 | Safari浏览器、旧版Chrome浏览器(2013年前)。 | Chrome浏览器、Microsoft Edge浏览器、Node.js 服务端环境。 |
| 语言侧重 | 渲染、图形逻辑使用 C++ 实现。 | 编译器、运行时等核心部分使用 C++ 实现。 |