导航到渲染:浏览器加载页面的关键路径分析

浏览器加载页面的关键路径分析

从输入 URL 到页面渲染的细步骤解析,分为 网络请求阶段解析与构建阶段渲染与合成阶段;

详细流程如下:

1. 用户输入 URL → 触发导航流程
   │
2. URL 解析 → 分解协议、域名、路径等
   │
3. 检查缓存(Service Worker、Memory Cache、Disk Cache)
   │
4. DNS 解析 → 递归查询获取 IP 地址
   │
5. 建立 TCP 连接 → 三次握手 + TLS 协商(HTTPS)
   │
6. 发送 HTTP 请求 → 包含请求头、缓存策略
   │
7. 服务器处理请求 → 返回响应头和资源
   │
8. 接收响应 → 处理分块传输(Chunked Encoding)
   │
9. 解析 HTML → 构建 DOM 树(Tokenization、Tree Construction)
   │
10. 预加载扫描 → 并行下载 CSS、JS、图片等
   │
11. 解析 CSS → 构建 CSSOM 树(选择器优先级、层叠规则)
   │
12. 执行 JavaScript → 编译、解释、执行(可能阻塞 DOM/CSSOM)
   │
13. 构建渲染树(Render Tree) → 合并 DOM + CSSOM,过滤不可见节点
   │
14. 布局计算(Layout/Reflow) → 计算几何属性(盒模型、浮动、定位)
   │
15. 分层(Layerization) → 创建合成层(GPU 加速)
   │
16. 绘制(Paint) → 生成绘制指令(Skia 等图形库)
   │
17. 栅格化(Rasterization) → 将指令转为位图(CPU/GPU 协作)
   │
18. 合成(Composite) → 合并图层并显示(VSync 信号同步)
   │
19. 事件绑定 → 注册交互事件监听器
   │
20. 持续更新 → 处理异步请求、动态 DOM 修改等

详细流程图如下: 

以上的流程图,比较细,以下流程图只涵盖了主要步骤的部分:

一、网络请求阶段

1. 用户输入 URL

  • 作用:触发导航流程,可能涉及页面跳转或新标签页打开。

  • 底层细节

2. URL 解析

  • 作用:分解 URL 的各个组成部分。

  • 解析结果

    • 协议(Scheme):httphttpsfile 等。

    • 域名(Host):www.example.com

    • 端口(Port):默认 HTTP 为 80,HTTPS 为 443。

    • 路径(Path):/path/to/resource

    • 查询参数(Query):?key=value

    • 哈希(Hash):#section(不发送到服务器)。

3. 检查缓存

  • 作用:尽可能复用本地资源,减少网络请求。

  • 缓存层级

    1. Service Worker 缓存:若注册了 Service Worker,优先拦截请求。

    2. Memory Cache:存储短期资源(如页面导航间的资源)。

    3. Disk Cache:持久化存储(根据 Cache-Control 头)。

  • 缓存策略

    • 强缓存Cache-Control: max-age=3600,直接使用缓存,不请求服务器。

    • 协商缓存:发送 If-Modified-Since 或 ETag 到服务器验证。

4. DNS 解析

  • 作用:将域名转换为 IP 地址。

  • 查询流程

    1. 浏览器缓存 → 操作系统缓存 → 路由器缓存 → ISP DNS 服务器。

    2. 若未命中,向根域名服务器(Root DNS)发起递归查询:

      • 根 DNS 返回顶级域名服务器(TLD DNS,如 .com)。

      • TLD DNS 返回权威域名服务器(Authoritative DNS)。

      • 权威 DNS 返回最终 IP。

  • 优化

    • DNS 预解析:通过 <link rel="dns-prefetch" href="//example.com"> 提前解析。

    • DNS over HTTPS (DoH):加密 DNS 查询,防止劫持。

5. 建立 TCP 连接

  • 作用:确保可靠的数据传输通道。

  • 三次握手流程

    1. SYN:客户端发送 SYN 包(序列号 x)。

    2. SYN-ACK:服务器返回 SYN-ACK 包(序列号 y,确认号 x+1)。

    3. ACK:客户端发送 ACK 包(确认号 y+1)。

  • HTTPS 的 TLS 握手

    1. Client Hello:客户端支持的 TLS 版本、加密套件、随机数。

    2. Server Hello:服务器选择的加密套件、随机数、证书。

    3. 证书验证:客户端验证证书链、有效期、吊销状态(CRL/OCSP)。

    4. 密钥交换:通过非对称加密(如 RSA、ECDHE)生成会话密钥。

    5. Finished:双方切换至对称加密通信。

6. 发送 HTTP 请求

  • 作用:向服务器请求资源。

  • 请求组成

    • 请求行GET /index.html HTTP/1.1

    • 请求头

      • User-Agent:浏览器标识。

      • Accept:可接受的 MIME 类型(如 text/html)。

      • Cookie:携带会话信息。

      • If-None-Match:协商缓存验证。

    • 请求体POST 请求时包含数据(如表单提交)。

7. 服务器处理请求

  • 作用:生成响应数据。

  • 处理流程

    1. Web 服务器(如 Nginx)接收请求,路由到后端应用(如 Node.js)。

    2. 后端处理业务逻辑(数据库查询、API 调用等)。

    3. 生成响应头和内容(HTML、JSON 等)。

  • 响应头关键字段

    • Content-Typetext/html; charset=utf-8

    • Content-Encodinggzip(压缩方式)。

    • Set-Cookie:设置客户端 Cookie。

8. 接收响应

  • 作用:处理服务器返回的字节流。

  • 分块传输(Chunked Encoding)

    • 服务器分块发送数据(Transfer-Encoding: chunked)。

    • 浏览器逐步接收并处理,提升首屏加载速度。

  • 解压缩:若响应头包含 Content-Encoding: gzip,浏览器解压数据。


二、解析与构建阶段

9. 解析 HTML(构建 DOM 树)

  • 作用:将 HTML 转换为树状结构的 DOM。

  • 解析流程

    1. 字节流解码:根据 charset(如 UTF-8)转换为字符流。

    2. 词法分析(Tokenization):生成 Tokens(如 <div></div>)。

    3. 语法分析(Tree Construction)

      • 使用 栈结构 维护节点层级。

      • 遇到 <script> 时:

        • 若未标记 async 或 defer,暂停解析,下载并执行脚本。

        • 若标记 async,异步下载,下载完成后立即执行。

        • 若标记 defer,异步下载,延迟到 DOMContentLoaded 前执行。

    4. 生成 DOM 树:最终形成文档对象模型。

10. 预加载扫描(Preload Scanner)

  • 作用:提前发现并下载关键资源。

  • 优化机制

    • 扫描未解析的 HTML,发现 <img><link><script> 标签。

    • 并行下载 CSS、JS、图片等,避免阻塞主线程。

11. 解析 CSS(构建 CSSOM 树)

  • 作用:确定每个 DOM 节点的样式。

  • 解析流程

    1. 解析选择器:将 CSS 规则转换为内部表示(如从右向左解析 .class > a)。

    2. 计算层叠值

      • 根据优先级(内联 > ID > Class > 标签)和 !important 确定最终样式。

      • 处理继承属性(如 font-size 继承自父元素)。

    3. 构建 CSSOM:生成样式规则树,每个节点包含计算后的样式。

12. 执行 JavaScript

  • 作用:动态修改 DOM/CSSOM。

  • 引擎流程(以 V8 为例):

    1. 解析(Parsing):生成抽象语法树(AST)。

    2. 编译(Compilation):将 AST 转为字节码或机器码。

    3. 执行:在调用栈中执行代码,可能触发 DOM 操作。

  • 优化策略

    • 避免同步操作:使用 requestIdleCallback 执行非关键任务。

    • 代码分割:按需加载 JS 模块(如 Webpack 的 import())。


三、渲染与合成阶段

13. 构建渲染树(Render Tree)

  • 作用:合并 DOM 和 CSSOM,确定可见节点。

  • 筛选规则

    • 排除 <meta><script><head> 等不可见节点。

    • 排除 display: none 的元素(但保留 visibility: hidden)。

14. 布局计算(Layout/Reflow)

  • 作用:计算每个节点的几何信息。

  • 计算流程

    1. 盒模型计算:根据 widthpaddingbordermargin 确定尺寸。

    2. 定位模式

      • 正常流:元素按 HTML 顺序布局。

      • 浮动:脱离正常流,其他内容环绕。

      • 绝对定位:相对于最近的非 static 父元素定位。

    3. 布局树生成:生成包含位置信息的布局树。

15. 分层(Layerization)

  • 作用:将页面划分为多个图层,优化渲染性能。

  • 分层条件

    • 显式层:will-change: transformposition: fixed

    • 隐式层:内容溢出(overflow: scroll)、3D 变换(transform: translateZ(0))。

  • 图层树(Layer Tree):每个图层独立栅格化。

16. 绘制(Paint)

  • 作用:生成绘制指令列表。

  • 绘制顺序

    1. 背景颜色 → 背景图片 → 边框 → 文本 → 子元素。

  • 绘制指令

    • 如 drawRect(x, y, width, height, color)

    • 使用 Skia(Chrome)或 Direct2D(Edge)等图形库。

17. 栅格化(Rasterization)

  • 作用:将绘制指令转换为像素位图。

  • 优化技术

    • 增量栅格化:仅更新可视区域(Viewport)内的内容。

    • GPU 加速:通过 OpenGL/Vulkan 将复杂图形(如 CSS 动画)交给 GPU 处理。

18. 合成(Composite)

  • 作用:合并图层并显示到屏幕。

  • 合成线程(Compositor Thread)

    • 独立于主线程,避免阻塞。

    • 处理滚动、缩放等操作,直接复用现有图层。

  • 显示流程

    1. 合成线程提交帧数据到 GPU。

    2. GPU 通过 垂直同步(VSync) 信号刷新屏幕(通常 60Hz,即 16.6ms/帧)。


关键机制与性能优化

1. 重绘(Repaint)与回流(Reflow)

  • 回流触发条件

    • 修改几何属性(宽度、高度、边距)。

    • 调整窗口大小、字体更改、内容变化(如输入文本)。

  • 重绘触发条件

    • 修改颜色、背景、阴影等非几何样式。

  • 优化策略

    • 使用 transform 和 opacity 实现动画(触发合成,跳过布局和绘制)。

    • 批量 DOM 操作(如使用 DocumentFragment)。

2. 事件循环(Event Loop)

  • 任务队列

    • 宏任务(Macrotask)setTimeoutsetInterval、I/O。

    • 微任务(Microtask)Promise.thenMutationObserver

  • 渲染时机

    • 每完成一个宏任务后,执行所有微任务,随后可能触发渲染。


开发者工具分析

  • Performance 面板

    • 记录加载过程中的网络请求、JS 执行、布局、绘制时间。

    • 分析长任务(Long Tasks)和强制同步布局(Forced Synchronous Layouts)。

  • Layers 面板:查看页面分层情况,优化图层数量。


浏览器渲染总结

浏览器渲染是一个涉及多线程、多阶段协作的复杂流程,理解底层机制(如分层、合成、事件循环)和性能瓶颈(如回流、长任务)是优化关键。通过工具分析和代码策略(如减少 DOM 操作、利用 GPU 加速),可显著提升用户体验;

作为前端开发如何提升页面性能 

一、网络阶段优化

1. 减少 DNS 查询

  • 策略:使用 dns-prefetch 预解析关键域名。

  • 实现

    <link rel="dns-prefetch" href="//cdn.example.com">

2. 利用缓存

  • 策略:设置强缓存和协商缓存。

  • 实现

    • 服务器配置(Nginx)

      location /static/ {
        expires 365d;          # 强缓存一年
        add_header Cache-Control "public";
      }
    • Service Worker:缓存关键资源。

      self.addEventListener('install', (e) => {
        e.waitUntil(
          caches.open('v1').then(cache => cache.addAll(['/app.js', '/style.css']))
        );
      });

3. 压缩与分块传输

  • 策略:启用 Gzip/Brotli 压缩和分块传输。

  • 实现

    gzip on;
    gzip_types text/css application/javascript;

4. 减少 HTTP 请求

  • 策略

    • 合并小文件(如雪碧图、Webpack 打包)。

    • 使用 HTTP/2 多路复用。

  • 实现

    # Webpack 配置合并 JS/CSS
    module.exports = {
      optimization: { splitChunks: { chunks: 'all' } }
    };

二、解析与构建阶段优化

1. 避免渲染阻塞

  • 策略

    • 异步加载非关键 CSS/JS。

    • 内联关键 CSS(Critical CSS)。

  • 实现

    <!-- 异步加载 JS -->
    <script src="app.js" defer></script>
    
    <!-- 内联关键 CSS -->
    <style>
      .header { ... } /* 首屏样式 */
    </style>

2. 优化 HTML 结构

  • 策略:减少 DOM 嵌套层级,避免复杂选择器。

  • 示例

    <!-- 避免 -->
    <div><div><div>...</div></div></div>
    <!-- 推荐 -->
    <div class="container">...</div>

3. CSS 优化

  • 策略

    • 使用 Flex/Grid 替代浮动布局。

    • 避免 @import(阻塞加载)。

    • 减少选择器复杂度(如避免 .a .b .c)。

  • 实现

    /* 避免 */
    .nav ul li a { ... }
    /* 推荐 */
    .nav-link { ... }

4. JavaScript 优化

  • 策略

    • 代码分割(Code Splitting)。

    • 按需加载(Lazy Load)。

  • 实现

    // Webpack 动态导入
    import('./module.js').then(module => { ... });
    
    // 图片懒加载
    <img data-src="image.jpg" class="lazyload">
    <script>
      const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
          if (entry.isIntersecting) {
            const img = entry.target;
            img.src = img.dataset.src;
            observer.unobserve(img);
          }
        });
      });
      document.querySelectorAll('.lazyload').forEach(img => observer.observe(img));
    </script>

三、渲染与合成阶段优化

1. 减少重绘与回流

  • 策略

    • 批量 DOM 操作。

    • 使用 transform 和 opacity 实现动画。

  • 实现

    // 批量修改 DOM
    const fragment = document.createDocumentFragment();
    for (let i = 0; i < 100; i++) {
      const div = document.createElement('div');
      fragment.appendChild(div);
    }
    document.body.appendChild(fragment);
    
    // GPU 加速动画
    .animate-box {
      transform: translateZ(0); /* 触发 GPU 加速 */
      transition: transform 0.3s;
    }

2. 优化图层管理

  • 策略:合理使用 will-change 和 contain 属性。

  • 实现

    .scroll-container {
      will-change: transform; /* 提前告知浏览器可能变化 */
      contain: strict;        /* 限制渲染边界 */
    }

3. 优化事件处理

  • 策略:防抖(Debounce)和节流(Throttle)。

  • 实现

    // 节流滚动事件
    window.addEventListener('scroll', throttle(() => {
      console.log('Scrolling...');
    }, 200));
    
    function throttle(fn, delay) {
      let last = 0;
      return (...args) => {
        const now = Date.now();
        if (now - last > delay) {
          fn.apply(this, args);
          last = now;
        }
      };
    }

四、工具与监控

1. 性能分析工具

  • Lighthouse:生成性能报告并提供优化建议。

    lighthouse http://example.com --view
  • Chrome DevTools

    • Performance 面板:分析任务耗时。

    • Layers 面板:查看图层分层情况。

2. 性能监控

  • Core Web Vitals:监控关键指标(LCP、FID、CLS)。

    // 上报 CLS(布局偏移)
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        console.log('CLS:', entry.value);
      }
    });
    observer.observe({ type: 'layout-shift', buffered: true });

3. 构建工具优化

  • Webpack 配置

    • Tree Shaking:删除未使用代码。

    • 压缩混淆:TerserPlugin 和 CSSNano。

      // webpack.config.js
      module.exports = {
        optimization: {
          minimize: true,
          minimizer: [new TerserPlugin(), new CssMinimizerPlugin()],
        },
      };

五、框架级优化(以 React 为例)

1. 减少不必要的渲染

  • 策略:使用 React.memouseMemouseCallback

    const MemoComponent = React.memo(({ data }) => {
      return <div>{data}</div>;
    });
    
    function App() {
      const data = useMemo(() => computeExpensiveValue(), []);
      return <MemoComponent data={data} />;
    }

2. 虚拟化长列表

  • 策略:使用 react-window 或 react-virtualized

    import { FixedSizeList as List } from 'react-window';
    const Row = ({ index, style }) => <div style={style}>Row {index}</div>;
    <List height={600} itemSize={35} itemCount={1000}>{Row}</List>;

3. 代码分割

  • 策略:动态加载组件(React.lazy + Suspense)。

    const LazyComponent = React.lazy(() => import('./LazyComponent'));
    function App() {
      return (
        <Suspense fallback={<Spinner />}>
          <LazyComponent />
        </Suspense>
      );
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值