浏览器面试题
浏览器架构
浏览器的多进程架构是什么?
主要进程:
- 浏览器主进程:界面显示、用户交互、子进程管理
- 渲染进程:页面渲染、JS 执行(每个标签页一个)
- GPU 进程:3D 绘制、CSS 动画
- 网络进程:网络资源加载
- 插件进程:浏览器插件
优势:
- 进程隔离,一个标签页崩溃不影响其他
- 安全隔离
- 充分利用多核 CPU
渲染进程包含哪些线程?
线程:
- 主线程:JS 执行、DOM 解析、样式计算、布局、绘制
- 合成线程:图层合成
- 光栅化线程:图层分块、光栅化
- Worker 线程:Web Worker
渲染流程
浏览器渲染流程是什么?
流程:
- HTML 解析:构建 DOM 树
- CSS 解析:构建 CSSOM 树
- 构建渲染树:DOM + CSSOM → Render Tree
- 布局(Layout):计算元素位置和大小
- 绘制(Paint):绘制到图层
- 合成(Composite):图层合成,输出到屏幕
什么是关键渲染路径?如何优化?
关键渲染路径:
- HTML → DOM
- CSS → CSSOM
- DOM + CSSOM → Render Tree
- Render Tree → Layout → Paint → Composite
优化:
- 减少关键资源数量
- 减少关键资源大小
- 缩短关键渲染路径长度
- 内联关键 CSS
- 延迟非关键 CSS
CSS 和 JS 如何影响渲染?
CSS:
- 不阻塞 DOM 解析
- 阻塞渲染(需要 CSSOM)
- 应该放在
<head>中
JavaScript:
- 阻塞 DOM 解析
- 阻塞渲染
- 应该放在
</body>前 - 或使用
async、defer
<!-- 异步加载,不阻塞解析 -->
<script async src="script.js"></script>
<!-- 延迟执行,不阻塞解析 -->
<script defer src="script.js"></script>
重排和重绘
什么是重排(Reflow)和重绘(Repaint)?
重排(Reflow):
- 改变元素尺寸、位置、布局
- 触发条件:修改宽高、位置、字体大小等
- 性能开销大
重绘(Repaint):
- 改变元素外观,不影响布局
- 触发条件:修改颜色、背景、边框等
- 性能开销较小
合成(Composite):
- 使用
transform、opacity触发 - 由 GPU 处理,性能最好
- 不触发重排和重绘
如何减少重排和重绘?
方法:
- 使用
transform和opacity(触发合成层) - 批量 DOM 操作(DocumentFragment)
- 避免频繁读取布局属性
- 使用
will-change提示浏览器 - 使用虚拟滚动处理长列表
// ❌ 不好:多次重排
element.style.width = '100px';
element.style.height = '100px';
// ✅ 好:一次重排
element.style.cssText = 'width: 100px; height: 100px;';
// ✅ 更好:使用 class
element.className = 'new-style';
哪些属性会触发重排?哪些会触发重绘?
触发重排:
width、height、padding、marginposition、top、left、right、bottomfont-size、line-heightdisplay、visibility- 读取布局属性(
offsetTop、scrollTop等)
触发重绘:
color、background-colorborder、outlinevisibility
不触发(合成层):
transformopacityfilter
浏览器缓存
浏览器缓存有哪些类型?
HTTP 缓存:
- 强缓存:Cache-Control、Expires
- 协商缓存:ETag、Last-Modified
浏览器存储:
- Cookie(4KB)
- LocalStorage(5-10MB)
- SessionStorage(5-10MB)
- IndexedDB(较大)
Service Worker 缓存:
- 可编程缓存
- 离线可用
HTTP 缓存的流程是什么?
强缓存流程:
1. 检查 Cache-Control/Expires
2. 未过期 → 使用缓存(200 from cache)
3. 已过期 → 进入协商缓存
协商缓存流程:
1. 发送请求,携带 If-None-Match / If-Modified-Since
2. 服务器比较
3. 未改变 → 304 Not Modified(使用缓存)
4. 已改变 → 200 OK(返回新资源)
Cookie、LocalStorage、SessionStorage 的区别?
| 特性 | Cookie | LocalStorage | SessionStorage |
|---|---|---|---|
| 大小 | 4KB | 5-10MB | 5-10MB |
| 生命周期 | 可设置过期时间 | 持久化 | 会话级别 |
| 作用域 | 可设置域名、路径 | 同源 | 同源 |
| 请求携带 | 是 | 否 | 否 |
| 访问方式 | document.cookie | localStorage API | sessionStorage API |
如何选择合适的缓存策略?
静态资源:
Cache-Control: public, max-age=31536000, immutable
HTML:
Cache-Control: no-cache
API:
Cache-Control: private, max-age=300
事件机制
事件流是什么?
三个阶段:
- 捕获阶段:从 window 到目标元素
- 目标阶段:在目标元素上
- 冒泡阶段:从目标元素到 window
事件委托:
// 利用事件冒泡
container.addEventListener('click', (e) => {
if (e.target.matches('.item')) {
handleClick(e);
}
});
如何阻止事件冒泡和默认行为?
// 阻止冒泡
event.stopPropagation();
// 阻止默认行为
event.preventDefault();
// 阻止捕获和冒泡
event.stopImmediatePropagation();
同源策略
什么是同源策略?
同源条件:
- 协议相同
- 域名相同
- 端口相同
限制:
- Cookie、LocalStorage、IndexedDB 无法跨域访问
- DOM 无法跨域访问
- AJAX 请求受同源策略限制
如何解决跨域问题?
方法:
- CORS:服务器设置响应头
- JSONP:利用
<script>标签 - 代理:开发环境使用代理
- postMessage:跨窗口通信
- WebSocket:不受同源策略限制
性能指标
如何测量页面性能?
Performance API:
const timing = performance.timing;
const pageLoadTime = timing.loadEventEnd - timing.navigationStart;
Performance Observer:
new PerformanceObserver((list) => {
const entries = list.getEntries();
// 处理性能指标
}).observe({ entryTypes: ['largest-contentful-paint'] });
Web Vitals 包含哪些指标?
Core Web Vitals:
- LCP (Largest Contentful Paint):最大内容绘制,< 2.5s
- FID (First Input Delay):首次输入延迟,< 100ms
- CLS (Cumulative Layout Shift):累积布局偏移,< 0.1
常见问题
为什么 JS 会阻塞 DOM 解析?
原因:
- JS 可能修改 DOM
- 浏览器需要等待 JS 执行完成
- 避免重复解析
解决:
- 使用
async、defer - 将 JS 放在
</body>前 - 使用动态导入
为什么 CSS 不阻塞 DOM 解析但阻塞渲染?
原因:
- DOM 解析和 CSS 解析可以并行
- 但渲染需要 DOM 和 CSSOM 都准备好
- 所以 CSS 阻塞渲染
浏览器如何优化渲染性能?
优化:
- 合成层:使用 GPU 加速
- 图层合并:减少图层数量
- 重排优化:批量处理
- 重绘优化:使用合成属性
如何调试浏览器性能问题?
工具:
- Chrome DevTools Performance
- Lighthouse
- Network 面板
- Memory 面板
方法:
- 录制性能
- 分析时间线
- 找出性能瓶颈
- 优化代码