导航中会发生什么
当我们在浏览器中输入网址,浏览器从互联网提取数据并显示网页,这中间都发生了什么?
第 1 阶段,处理用户输入
当用户在地址栏中输入内容时,界面线程首先判断这是搜索关键字还是URL
- 如果是网址,则进入导航流程
- 如果是关键词,则交给默认搜索引擎处理
第 2 阶段,检查 Service Worker 和缓存
浏览器正式发起请求前,会优先进行以下检查:
- Service Worker 是否注册并拦截请求(若有,自定义响应或继续请求)
- 强缓存(Cache-Control) 是否命中(若命中,直接使用缓存资源)
这一步可以完全跳过网络请求,是性能优化的第一道防线。
第 3 阶段,网络请求过程
若没有被拦截或缓存命中,则进入网络请求流程:
- DNS 解析:将域名解析为 IP 地址(可能走本地缓存)
- TCP 连接建立:三次握手;若是 HTTPS,还要进行 TLS 握手
- 发送 HTTP 请求:包括请求头、Cookie、User-Agent 等
- 服务器返回响应:可能是 200 成功,也可能是 301/302 重定向(会重新发起请求)
第 4 阶段,响应处理与渲染
渲染程序的核心工作是将 HTML、CSS 和 JavaScript 转换为用户可以与之互动的网页,浏览器渲染是一个流水线式的过程。
- HTML 解析和 DOM 构建:将 HTML 转化为 DOM,DOM 是浏览器对网页的内部表示
- 子资源加载:如果 HTML 文档中存在
<img>
或<link>
等内容,预加载扫描器会查看 HTML 解析器生成的令牌,并向浏览器进程中的网络线程发送请求。 - JavaScript 阻止解析:当 HTML 解析器遇到
<script>
标记时,它会暂停解析 HTML 文档,并必须加载、解析和执行 JavaScript 代码,这个过程可能引发的回流与重绘,这是性能优化的重要环节。 - 资源优先级:
<link rel="preload">
可以告知浏览器需要尽快下载该资源。
JavaScript 可以阻止解析
当 HTML 解析器发现一个 <script>
标签时,它会暂停对 HTML 文档的解析,并加载、解析和执行 JavaScript 代码。因为 JavaScript 可以使用诸如 document.write()
之类的功能来改变文档的结构,从而改变整个 DOM 结构,因此 HTML 解析器必须等待 JavaScript 运行完毕,然后才能恢复对 HTML 文档的解析。为了提高页面加载性能,通常把 <script>
标签放在 <body>
标签的底部。
如果不希望阻塞页面加载,也可以使用 <script>
标签的 async 和 defer 属性。
- async 表示异步加载和执行,不会阻塞页面的呈现
- defer 表示将在 HTML 文档解析完成后执行
- CSS 解析和样式计算:解析 CSS 并确定每个 DOM 节点的样式
- 浏览器有默认的 CSS 样式表:https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/html/resources/html.css
- 样式计算不会真正渲染页面,但它是布局阶段的前提,结果保存在 ComputedStyle 对象中。
- 布局:主线程会遍历 DOM 和计算样式,并创建包含 x、y 坐标和边界框大小等信息的布局树
- 如果应用了
display: none
,则该元素不属于布局树,具有visibility: hidden
的元素属于布局树
- 绘制:知道元素的大小、形状和位置,但仍需要判断绘制它们的顺序
- 主线程会遍历布局树以创建绘制记录,先绘制背景,然后绘制文本,最后绘制矩形
- 合成与光栅化:将网页的各个部分拆分为图层、单独光栅化这些图层,并在合成程序线程的单独线程中将其合成为网页
- 为了确定哪些元素需要位于哪些层中,主线程会遍历布局树以创建层树
- 如果网页的某些部分应为单独的层(例如滑入式侧边菜单),但未获得单独的层,则您可以在 CSS 中使用
will-change
属性向浏览器发出提示,开发中常通过will-change
或transform
强制触发独立图层
光栅化就是把矢量图形转换成一堆像素点,光栅线程会对每个图块进行光栅化处理,并将其存储在 GPU 显存中
交互优化
页面滚动性能优化:所有 touchstart
、touchmove
监听器都应加 { passive: true }
,可以避免阻塞滚动,保持合成器线程的流畅性。
高频事件监听器(如画板):使用 requestAnimationFrame
合并更新 + getCoalescedEvents
提高精度。
document.body.addEventListener('touchstart', event => {
if (event.target === area) {
event.preventDefault();
}
});
单页应用大量事件委托:避免全页面绑定事件 + preventDefault
,否则整个网页都会被标记为非快速滚动区域。
页面首屏加载优化:使用功能策略防止阻塞脚本(如 sync-script: none
)。
参考文章
https://developer.chrome.com/blog/inside-browser-part2/