网页关键渲染路径

详细请阅读 google文档 ,这儿记录一些关键的总结
udacity 有个课程也讲得很详细: 课程

浏览器首次渲染的几个步骤

  1. 处理 HTML 标记并构建 DOM 树。
  2. 处理 CSS 标记并构建 CSSOM 树。
  3. DOMCSSOM 合并成一个渲染树。渲染树不包括不可见的元素(如: header 下所有,display:none; 的元素,但包括 visibility:hidden; 的元素)
  4. Layout : 根据渲染树来布局,以计算每个节点的几何信息。
  5. Paint : 将各个节点绘制到屏幕上。

优化关键渲染路径就是指最大限度缩短执行上述第 1 步至第 5 步耗费的总时间。 这样一来,就能尽快将内容渲染到屏幕上,此外还能缩短首次渲染后屏幕刷新的时间,即为交互式内容实现更高的刷新率。

检测并分析

工具

  1. Lighthouse Chrome 扩展程序
  2. Navigation Timing API:

    首先了解一下页面加载时的浏览器事件:
    浏览器加载过程
    几个关键的事件:

    • domInteractive 表示 DOM 准备就绪的时间点。
    • domContentLoaded 一般表示 DOM 和 CSSOM 均准备就绪的时间点。(如果没有阻塞解析器的 JavaScript,则 DOMContentLoaded 将在 domInteractive 后立即触发。)
    • domComplete 表示网页及其所有子资源都准备就绪的时间点。

      示例代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      <!DOCTYPE html>
      <html>
      <head>
      <title>Critical Path: Measure</title>
      <meta name="viewport" content="width=device-width,initial-scale=1">
      <link href="style.css" rel="stylesheet">
      <script>
      function measureCRP() {
      var t = window.performance.timing,
      interactive = t.domInteractive - t.domLoading,
      dcl = t.domContentLoadedEventStart - t.domLoading,
      complete = t.domComplete - t.domLoading;
      var stats = document.createElement('p');
      stats.textContent = 'interactive: ' + interactive + 'ms, ' +
      'dcl: ' + dcl + 'ms, complete: ' + complete + 'ms';
      document.body.appendChild(stats);
      }
      </script>
      </head>
      <body onload="measureCRP()">
      <p>Hello <span>web performance</span> students!</p>
      <div><img src="awesome-photo.jpg"></div>
      </body>
      </html>

实例分析

还是看 google开发文档 ,特别详细清晰。

优化方案

当有 block 的资源时, CRP(Critical Rendering Path) 如图:
crp-progress

所以,我们优化关键渲染路径的常规步骤如下:

  1. 对关键路径进行分析和特性描述,三个关键渲染路径指标:资源数、字节数、长度。
  2. 最大限度减少关键资源的数量:删除它们,延迟它们的下载,将它们标记为异步等。
  3. 优化关键字节数以缩短下载时间(往返次数,Tcp 会对大文件进行多次往返)。
  4. 优化其余关键资源的加载顺序:您需要尽早下载所有关键资产,以缩短关键路径长度。

注意: 这些资源并不是 blocked : images

优化 JavaScript 的使用

无论我们使用 <script> 标记还是内联 JavaScript 代码段,您都可以期待两者能够以相同方式工作。 在两种情况下,浏览器都会先暂停构建 DOM 并执行脚本,然后才会处理剩余文档。

由于 JavaScript 还可以操作 CSS ,阻塞解析器的 JavaScript 会强制浏览器等待 CSSOM 构建完成并暂停 DOM 的构建,继而大大延迟首次渲染的时间。 如果是外部 JavaScript 文件,浏览器必须停下来,等待从磁盘、缓存或远程服务器获取脚本,这就可能给关键渲染路径增加数十至数千毫秒的延迟。

所以JavaScript 资源会阻塞解析器,除非将其标记为 async 或通过专门的 JavaScript 代码段进行添加。

  1. 使用异步 JavaScript 资源
    向某些 script 标记添加异步关键字 async ,如分析工具的 Js 。这样就不会 block DOM 的构建,也不会 block CSSOM 的构建。

    1
    <script src="app.js" async></script>

    在之前没有 async 的时候,我们一般将脚本写在 onload 事件里,这样可以减少 block 的时间。

    1
    2
    3
    window.onload = function() {
    // do something
    }
  2. 延迟解析非必需的 JavaScript
    为了最大限度减少浏览器渲染网页的工作量,应延迟任何非必需的脚本(即对构建首次渲染的可见内容无关紧要的脚本)。

  3. 避免运行时间长的 JavaScript
    运行时间长的 JavaScript 会阻止浏览器构建 DOMCSSOM 以及渲染网页,所以任何对首次渲染无关紧要的初始化逻辑和功能都应延后执行。如果需要运行较长的初始化序列,请考虑将其拆分为若干阶段,以便浏览器可以间隔处理其他事件。

优化 CSS 的使用

CSS 是构建渲染树的必备元素,首次构建网页时, JavaScript 常常受阻于 CSS。确保将任何非必需的 CSS 都标记为非关键资源(例如打印和其他媒体查询),并应确保尽可能减少关键 CSS 的数量,以及尽可能缩短传送时间。

  1. CSS 置于文档 head 标签内
    尽早在 HTML 文档内指定所有 CSS 资源,以便浏览器尽早发现 <link> 标记并尽早发出 CSS 请求。
  2. 避免使用 CSS import
    一个样式表可以使用 CSS import (@import) 指令从另一样式表文件导入规则。不过,应避免使用这些指令,因为它们会在关键路径中增加往返次数:只有在收到并解析完带有 @import 规则的 CSS 样式表之后,才会发现导入的 CSS 资源。
  3. 内联阻塞渲染的 CSS
    为获得最佳性能,您可能会考虑将关键 CSS 直接内联到 HTML 文档内。这样做不会增加关键路径中的往返次数,并且如果实现得当,在只有 HTML 是阻塞渲染的资源时,可实现“一次往返”关键路径长度。
  4. 通过媒体查询减少会 block 首次加载的 CSS 。如:

    1
    2
    <link rel="stylesheet" href="style.css">
    <link rel="stylesheet" href="style-print.css" media="print">

    这样的话, 浏览器仍然会下载两个 CSS 文件,但是首次关键路径渲染不会让 style-print.css block 住, 只有在 print (打印) 时才会渲染 style-print.css

综合实例

这儿有个题目: udacity/frontend-nanodegree-mobile-portfolio

这是我的作业: jintangWang/frontend-nanodegree-mobile-portfolio

其他

  • 迁徙到 http2
  • webpack 代码拆分
堂 wechat
欢迎关注我的微信公众号,里面有各种小故事哦!
坚持原创技术分享,您的支持将鼓励我继续创作!