1、浏览器从输入 URL 到页面渲染完成,发生了什么?
当输入 URL并回车后,浏览器会先解析URL并查缓存,若未命中则进行 DNS 解析、TCP 三次握手(HTTPS 还要进行 TLS 握手),然后发出HTTP请求,服务器返回HTML。浏览器从流式HTML解析构建DOM,同时并下载解析CSS构建CSSDOM,合并成渲染树,经过布局、绘制、合成后显示页面。过程中JS会阻塞解析,CSS会阻塞渲染。性能优化可从减少请求数、压缩资源、利用缓存、优化关键渲染路径等方面入手。
具体步骤:
- 输入 URL → 请求发出
- 解析 URL:
拆分协议(HTTP/HTTPS)、域名、端口、路径、查询参数等 - 浏览器检查缓存(强缓存):
Memory Cache(内存缓存)、Disk Cache(硬盘缓存),如果命中Cache-Control: max-age 或 Expires,直接用缓存,跳过网络请求。 - DNS 解析:
- 将域名解析为
IP(可能走本地缓存/系统缓存/DNS服务器) - 支持
DNS Prefetch提前解析
- 将域名解析为
TCP三次握手:建立连接(如果是HTTPS,还要进行TLS握手)- 发送
HTTP请求:请求行 + 请求头(Cookie、User-Agent等);GET/POST等方法 - 服务器处理并返回响应:响应头(状态码、缓存策略、内容类型);响应体(
HTML、JSON、图片等) - 协商缓存(如命中
ETag或Last-Modified)
- 浏览器解析 HTML
- 构建 DOM 树
- 按字节流解析 HTML → Token → Node → DOM
- 遇到 CSS/JS 会影响解析:
- CSS:不会阻塞 DOM 构建,但会阻塞渲染
- JS:默认阻塞 DOM 解析(除非加 defer/async)
- 构建 CSSOM(CSS 对象模型)
- 解析外部 CSS 文件、
<style>、行内样式 - CSS 下载是异步的,但渲染前必须完成
- 解析外部 CSS 文件、
- 合并 DOM + CSSOM → Render Tree(渲染树)
- 布局与绘制
- 布局(Layout / Reflow)
- 根据渲染树计算每个元素的位置和大小
- 绘制(Paint)
- 将每个节点绘制成像素
- 合成(Composite Layers)
- 将不同图层合成,利用
GPU加速 - 触发机制:
position: fixed、transform、will-change等
- 将不同图层合成,利用
- JS 执行与交互
- 遇到
<script>会进入JS引擎执行 - 如果
JS修改了DOM/CSS,可能触发重排(Reflow)或重绘(Repaint)
- 性能优化可切入的点
- 减少
DNS查询→ DNS 预解析(<link rel="dns-prefetch">) - 减少
HTTP请求数 → 合并资源、使用HTTP/2 - 压缩资源 →
gzip/brotli - 利用缓存 →
Cache-Control、ETag - 优化关键渲染路径 →
Critical CSS、首屏SSR - 异步加载
JS→defer/async - 懒加载资源 → 图片、非首屏模块懒加载
2、浏览器的渲染流程是怎样的?
- 解析
HTML→ 构建DOM树- 浏览器的
HTML解析器按字节流读取HTML - 转成
Tokens(标签、文本、注释等) - 再转成
DOM节点对象 - 依次插入形成
DOM Tree(文档对象模型),遇到<script>(无 defer/async)会阻塞HTML解析 - 遇到外部
CSS会阻塞后续JS执行(因为JS可能依赖样式信息)
- 浏览器的
- 解析
CSS→ 构建CSSOM- 下载外部
CSS 文件(并解析<style>标签和行内样式) - 将选择器解析成节点规则,形成
CSS Object Model CSSOM与DOM是两棵独立的树,CSSOM是渲染的必要条件,它会阻塞首次渲染
- 下载外部
- 合并
DOM + CSSOM → Render Tree(渲染树),这棵树是后续布局和绘制的基础- 渲染树包含:
DOM中的可见节点(display: none的不会进来);每个节点的计算后样式(Computed Style)
- 渲染树包含:
- Layout(布局 / 回流)
- 根据渲染树计算每个节点的:
坐标位置(x/y)和尺寸(width/height) - 结果是一个带有几何信息的布局树
- 触发 Layout 的典型情况:
DOM结构变化(增加/删除节点);元素尺寸、位置变化;浏览器窗口尺寸变化
- 触发 Layout 的典型情况:
- 根据渲染树计算每个节点的:
- Paint(绘制)
- 根据布局结果,把元素的文字、颜色、边框、阴影、背景图等画到位图中
- 每个图层分别绘制:触发
Paint(但不回流)的情况:color、background-color、box-shadow等改变
- Composite(合成)
- 把多个图层(Layer)交给 GPU 合成
- 利用 硬件加速 渲染到屏幕
- 涉及滚动、CSS 动画、3D 变换等
注:HTML 和 CSS 解析是并行的,是在两个线程中进行解析的
3、浏览器的主线程负责哪些事情?还有哪些辅助线程?
答:主线程是“大脑”,管 DOM、CSSOM、JS、布局、绘制;其他线程是“手脚”,管下载、计时、事件、GPU、计算。
主线程(Main Thread):主线程基本上是浏览器渲染引擎的“大脑”,它会串行执行这些核心任务:
HTML解析- 由
HTML Parser执行 - 把
HTML转换为DOM 树 - 解析过程中遇到
<script>、<link>、<style>会交给对应模块处理
- 由
CSS解析- 有时主线程直接解析
CSS(小型/内联 CSS) - 大型 CSS 文件可能交给后台线程(多线程解析)
- 有时主线程直接解析
JavaScript执行- 调用
JS 引擎(V8) - 执行所有脚本(同步、异步回调、事件处理)
- 调用
样式计算(Style)- 把
CSS选择器匹配到DOM元素 - 生成计算样式(Computed Styles)
- 把
- 布局(Layout / Reflow)
- 根据
DOM + CSSOM计算每个元素的位置和大小
- 根据
- 绘制(Paint)& 合成(Composite)
- 绘制各个元素的位图
- 调用
GPU线程进行合成
4、什么是 Event Loop?微任务和宏任务分别是什么?它们的执行顺序?
答:Event Loop(事件循环) 是浏览器(或 Node.js)协调 同步任务 与 异步任务 执行顺序的机制。它的核心作用是:
- 同步任务 直接在主线程执行(调用栈中执行)
异步任务(定时器回调、事件回调、Promise 等)被放入不同的任务队列- 主线程空闲时,从任务队列取任务执行 → 这个不断循环的过程就是
Event Loop
宏任务(Macro Task):宏任务是一类大任务,每次执行时会完整地执行一段独立的代码,然后再进入下一轮循环。常见宏任务:setTimeout、setInterval、setImmediate(Node.js)、I/O 操作、UI 渲染任务、整个 script 脚本(第一次执行也算一个宏任务)
微任务(Micro Task):微任务是更细粒度的任务,它们会在当前宏任务结束后、下一个宏任务开始前立即执行。常见微任务:Promise.then/catch/finally、MutationObserver、queueMicrotask
执行顺序:
- 执行一个宏任务(可能是 script 整体、setTimeout 回调等)
- 执行完当前宏任务后,立刻执行所有微任务(直到清空微任务队列)
- 渲染 UI(如果需要)
- 进入下一轮宏任务 -> 先宏任务,后微任务;微任务全部执行完才进入下一轮宏任务。
script整体是第一个宏任务,Promise是微任务,setTimeout是下一个宏任务。
5、页面中的 JavaScript 执行会阻塞哪些阶段?为什么?
- 阻塞
HTML解析:- 同步
<script>标签会暂停HTML解析,先下载并执行JS,再继续解析。 - 原因:
JS可能调用document.write()或修改 DOM,影响后续解析内容。
- 同步
- 阻塞
CSSOM构建(间接影响JS执行)- 如果
JS在执行前需要读取样式信息(如getComputedStyle、布局数据),而CSS还没解析完成,JS必须等待CSS加载并构建CSSOM。 - 原因:保证样式数据是最新的,避免渲染结果错乱。
- 如果
- 阻塞渲染(Layout / Paint)
JS修改DOM或样式后,如果立即读取布局信息(如offsetHeight、scrollTop),会触发强制同步布局,阻塞渲染流水线。- 原因:浏览器必须立刻计算布局才能返回正确值。