浏览器渲染流程
1. 浏览器的整体渲染流程是什么?
答案: ① DNS 解析,建立 TCP 连接(TLS 握手);② 发送 HTTP 请求,接收响应;③ 解析 HTML 构建 DOM 树;④ 解析 CSS 构建 CSSOM 树;⑤ 合并 DOM 和 CSSOM 构建 Render Tree(只含可见节点);⑥ Layout(回流/重排):计算每个节点的位置和尺寸;⑦ Paint(重绘):生成绘制指令;⑧ Compositing(合成):将各层合并输出到屏幕。
2. 什么是 DOM 树和 CSSOM 树?
答案: DOM(Document Object Model)树是 HTML 解析后的树形表示,每个 HTML 元素是一个节点,JavaScript 通过 DOM API 操作页面结构。CSSOM(CSS Object Model)树是 CSS 解析后的树形表示,包含每个节点的完整样式信息(含继承和层叠计算结果)。只有两棵树都构建完成,浏览器才能生成 Render Tree 进行布局。
3. CSS 为什么会阻塞渲染?JS 呢?
答案: CSS 阻塞渲染:浏览器必须等 CSSOM 构建完成再构建 Render Tree,因此 CSS 加载会阻塞渲染(但不阻塞 HTML 解析)。同时 CSS 阻塞 JS 执行(JS 可能查询样式,需等 CSSOM 完成)。JS 阻塞 HTML 解析:<script> 标签会暂停 HTML 解析(可能修改 DOM),加载并执行完才继续,这是为什么 JS 推荐放在 </body> 前或使用 defer/async。
4. defer 和 async 的区别?
答案: 两者都异步下载脚本不阻塞 HTML 解析。区别:async 下载完立即执行(暂停 HTML 解析),执行顺序不保证;defer 在 HTML 解析完成后、DOMContentLoaded 事件前按顺序执行。推荐:普通脚本用 defer;独立脚本(如统计、广告)用 async;关键脚本放 <head> 不加属性。
5. 什么是回流(Reflow)和重绘(Repaint)?
答案: 回流(Reflow/Layout):元素的几何属性(位置、尺寸)发生变化,浏览器重新计算布局,成本最高。重绘(Repaint/Paint):样式改变但不影响布局(如颜色、背景),浏览器重新绘制,成本次之。合成(Compositing):只改变变换、透明度等可由 GPU 处理的属性,最高效,不触发回流和重绘。
6. 哪些操作容易触发回流?如何优化?
答案: 触发回流的操作:修改宽高、位置、字体大小、内外边距;增删 DOM 节点;读取 offsetWidth/scrollTop/getBoundingClientRect 等强制同步布局属性。优化:① 批量修改 DOM(DocumentFragment、className 一次性修改);② 避免在循环中读写布局属性(读写分离);③ 用 transform/opacity 代替 top/left 做动画;④ 使用 will-change 提示浏览器创建独立合成层。
7. 什么是合成层(Compositing Layer)?
答案: 浏览器将部分元素提升到独立的合成层,由 GPU 处理,不触发回流和重绘。触发条件:transform: translateZ(0)、will-change: transform、opacity 动画、position: fixed、<video>/<canvas>/<iframe>、有重叠需要处理的透明情况。过多合成层会占用大量内存,will-change 不要滥用。
8. 什么是 DOMContentLoaded 和 load 事件的区别?
答案: DOMContentLoaded:HTML 解析完成、DOM 树构建完毕时触发,不等待样式表、图片、子框架加载完成(但会等待 defer 脚本执行完成)。load:页面所有资源(图片、CSS、JS、iframe等)全部加载完成后触发。一般 DOM 操作在 DOMContentLoaded 监听,统计页面加载完成用 load。
9. 什么是 FCP、LCP、CLS、FID/INP?
答案: 这是 Core Web Vitals(核心网页指标):
- FCP(First Contentful Paint):首次有内容绘制到屏幕的时间,衡量感知加载速度
- LCP(Largest Contentful Paint):最大内容绘制时间,衡量主要内容加载速度(目标 <2.5s)
- CLS(Cumulative Layout Shift):累计布局偏移分数,衡量视觉稳定性(目标 <0.1)
- INP(Interaction to Next Paint,取代 FID):用户交互到下次绘制的延迟,衡量交互响应性(目标 <200ms)
10. 什么是关键渲染路径?如何优化?
答案: 关键渲染路径是浏览器从收到 HTML 到首次渲染的最短路径。优化方向:① 减少关键资源数量和体积(内联关键 CSS、压缩);② 减少关键路径长度(减少 RTT、CDN);③ 预加载关键资源(<link rel="preload">);④ 延迟非关键资源(defer、懒加载);⑤ 服务端渲染或流式渲染提前输出 HTML。
浏览器事件系统
11. 什么是事件捕获、目标阶段和事件冒泡?
答案: DOM 事件分三阶段:① 捕获阶段(从 window 向下到目标元素);② 目标阶段(在目标元素上触发);③ 冒泡阶段(从目标元素向上到 window)。addEventListener(event, handler, true) 在捕获阶段监听;默认(false/不传)在冒泡阶段监听。stopPropagation() 阻止向上冒泡,stopImmediatePropagation() 同时阻止同元素其他监听器。
12. 什么是事件委托?有什么好处?
答案: 事件委托利用事件冒泡,把子元素的事件监听器绑定在父元素上,通过 event.target 判断实际触发元素。好处:① 减少内存占用(一个监听器代替大量子元素监听器);② 动态新增子元素自动生效,无需重新绑定;③ 减少 DOM 操作次数。缺点:stopPropagation 会破坏委托;某些事件不冒泡(如 focus,需用 focusin 代替)。
13. event.preventDefault() 和 event.stopPropagation() 的区别?
答案: preventDefault():阻止元素的默认行为(如阻止链接跳转、表单提交、右键菜单),不影响事件传播。stopPropagation():阻止事件继续向上冒泡(或向下捕获),不影响元素默认行为。两者相互独立,可同时调用。return false(在 jQuery 中)等价于两者都调用,原生事件中只相当于 preventDefault()。
14. 什么是自定义事件(CustomEvent)?
答案: new CustomEvent('myEvent', { detail: { data: 123 }, bubbles: true, cancelable: true }) 创建自定义事件,通过 element.dispatchEvent(event) 分发,其他地方用 addEventListener('myEvent', handler) 监听。detail 字段传递自定义数据。适合跨组件、跨模块通信(原生发布订阅),不依赖框架。
浏览器存储
15. localStorage、sessionStorage 和 Cookie 的区别?
答案:
| 特性 | Cookie | localStorage | sessionStorage |
|---|---|---|---|
| 存储大小 | ~4KB | ~5-10MB | ~5-10MB |
| 过期时间 | 可设置 | 永久 | 标签页关闭 |
| 随请求发送 | 是(自动) | 否 | 否 |
| 可访问域 | 可跨子域 | 同源 | 同源+同标签页 |
| JS 可读 | 可被 HttpOnly 禁止 | 是 | 是 |
16. 什么是 IndexedDB?
答案: IndexedDB 是浏览器内置的 NoSQL 数据库,支持存储大量结构化数据(非字符串),支持索引、事务、游标。存储上限通常为磁盘的一定比例(远超 localStorage)。API 是异步的(基于事件,建议用 idb 等封装库)。适合离线 Web 应用、缓存大量数据(如邮件客户端、文档编辑器)。
17. 什么是 Cache API 和 Service Worker 缓存?
答案: Cache API 提供独立于 HTTP 缓存的可编程缓存机制,由 Service Worker 管理。Service Worker 是独立线程,可拦截所有网络请求,实现:离线缓存(PWA)、后台同步、推送通知。Cache API 存储 Request/Response 对,容量大,可精确控制缓存策略(Cache First、Network First、Stale-While-Revalidate)。
浏览器事件循环与性能
18. 浏览器的事件循环和 Node.js 有什么区别?
答案: 浏览器事件循环:每次循环取一个宏任务执行,执行完清空微任务队列,然后渲染(如需要),再取下一个宏任务。Node.js 事件循环:基于 libuv,有多个阶段(timers、pending callbacks、idle/prepare、poll、check、close),且有 process.nextTick(优先级高于 Promise 微任务)和 setImmediate(check 阶段)等特有 API。
19. 什么是 requestAnimationFrame?和 setTimeout 的区别?
答案: requestAnimationFrame(callback) 在下一帧绘制前执行回调,与浏览器刷新率同步(通常 60fps = 16.7ms/帧),适合动画。比 setTimeout(fn, 16) 好的地方:① 精确同步屏幕刷新,不丢帧;② 页面不可见时自动暂停(节省资源);③ 合并多个 RAF 回调,避免不必要的绘制。动画应优先用 RAF 或 CSS 动画而非 setInterval。
20. 什么是 requestIdleCallback?
答案: requestIdleCallback(callback) 在浏览器空闲时(主线程无高优先级任务时)执行回调,callback 接收 IdleDeadline 对象,可查询剩余空闲时间。适合非关键的后台任务(如数据上报、预加载、非紧急 DOM 操作)。React Fiber 架构受此启发,用 MessageChannel 模拟实现时间切片调度。
21. 什么是长任务(Long Task)?如何检测?
答案: 超过 50ms 的任务被称为长任务,会阻塞主线程导致页面卡顿。检测方式:① PerformanceObserver 监听 longtasks 条目;② Chrome DevTools Performance 面板(红色火焰表示长任务);③ performance.getEntriesByType('longtask')。优化:将长任务拆分为小任务(使用 scheduler.yield()、setTimeout(fn, 0)、requestIdleCallback)。
22. 什么是 Web Worker?
答案: Web Worker 在浏览器后台线程执行脚本,不阻塞主线程 UI,通过 postMessage 与主线程通信。Worker 无法访问 DOM,有独立的全局作用域。类型:① Dedicated Worker(专用,单页面);② Shared Worker(共享,多标签页);③ Service Worker(拦截网络请求,离线缓存)。适合 CPU 密集型任务:图像处理、数据压缩、复杂计算。
浏览器安全
23. 什么是点击劫持(Clickjacking)?如何防御?
答案: 攻击者用透明 iframe 覆盖正常页面,诱导用户点击实际上是 iframe 内目标站点的元素,执行非预期操作(如转账、授权)。防御:① 服务端设置 X-Frame-Options: DENY/SAMEORIGIN 禁止或限制被 iframe 嵌入;② CSP 的 frame-ancestors 'none' 指令;③ JS 判断 window.self !== window.top 时跳出 iframe(framebuster,可被绕过)。
24. 什么是 HTTPS 降级攻击?HSTS 如何防御?
答案: 中间人通过 SSL Stripping 将 HTTPS 连接降级为 HTTP,拦截明文数据。HSTS(HTTP Strict Transport Security)通过响应头 Strict-Transport-Security: max-age=31536000; includeSubDomains; preload 告诉浏览器该域名只能用 HTTPS 访问,并缓存此规则。浏览器在缓存期内自动将 HTTP 请求升级为 HTTPS,不给中间人降级机会。
25. 什么是子资源完整性(SRI)?
答案: SRI(Subresource Integrity)通过在 <link>/<script> 标签上加 integrity 属性(sha256-<base64hash>),让浏览器在加载资源后验证内容哈希是否匹配,防止 CDN 被篡改导致执行恶意脚本。不匹配则拒绝执行。使用第三方 CDN 时应配合 SRI 使用。
浏览器 API
26. IntersectionObserver 有什么用?
答案: IntersectionObserver 异步观察目标元素与视口(或祖先)的交叉状态,替代昂贵的 scroll 事件 + getBoundingClientRect() 方案。常用于:图片懒加载(进入视口才加载)、无限滚动、动画触发、广告曝光统计。性能远优于滚动事件监听,浏览器原生优化,不在主线程同步计算。
27. MutationObserver 有什么用?
答案: MutationObserver 异步监听 DOM 变化(子节点增删、属性变化、文本内容变化),替代已废弃的 Mutation Events(同步、性能差)。常用于:监听第三方代码对 DOM 的修改、虚拟 DOM 框架的 DOM diff 辅助、无障碍工具监听页面变化。每帧合并回调,性能友好。
28. ResizeObserver 有什么用?
答案: ResizeObserver 监听元素尺寸变化(区别于 window.resize 只监听视口),可精确监听任意元素大小变化。常用于:响应式图表(容器大小变化时重绘)、自适应组件、取代轮询判断元素大小。不触发回流,性能比 resize 事件 + getBoundingClientRect 好。
29. history API 和 hash 路由的区别?
答案: Hash 路由:URL 中 # 后的部分不发送到服务器,通过 hashchange 事件监听变化,兼容性好,服务器不需要配置。History 路由:使用 pushState/replaceState 修改 URL(无 #),通过 popstate 事件监听,URL 更美观,但需要服务器配置将所有路径返回 index.html,否则刷新 404。现代框架(React Router、Vue Router)默认推荐 History 模式。
30. 什么是浏览器的 Performance API?
答案: Performance API 提供高精度时间测量和性能数据。常用:performance.now()(比 Date.now() 精度更高);performance.mark()/measure()(自定义性能标记);PerformanceObserver 监听各类性能条目(LCP、FCP、longtasks、resource、navigation);performance.getEntriesByType('resource') 查询所有资源加载时序。是性能监控和优化的基础工具。
总结
这些题目覆盖了浏览器渲染流程、事件系统、存储机制、事件循环、性能优化、安全防护及常用 API,是深入理解前端运行环境的核心知识。