day12 简述浏览器的渲染过程及性能优化

80 阅读6分钟

每日一句

I love you three thousand.

释义:我爱你三千遍。

简述浏览器的渲染过程

  • 处理HTML标记构建DOM树
  • 处理CSS标记构建CSSOM树
  • 将DOM树和CSSOM树合成一个渲染树
  • 根据渲染树进行布局,计算机每个节点在浏览器中的位置及大小
  • 将每个节点都绘制到屏幕上

以上步聚并不一定按顺序执行,当DOM树,CSSOM树被css,js修改时,以上步聚会重复执行。

阻塞浏览器渲染

  • CSS阻塞渲染
  1. 因为CSS被视为阻塞渲染的资源,所以浏览器在CSSOM构建完毕之前,不会渲染任何已处理的内容。
  2. CSSOM 构建时,JavaScript执行将暂停,直到CSSOM 就绪。
  3. CSS 不会阻塞HTML的解析,但是会阻塞渲染树的构建 所以我们需要将它尽早、尽快地下载到客户端,以便缩短首次渲染的时间。 也就是说,我们要把引入css的标签放在head标签内。
  • JS阻塞渲染
  1. JS不加任何修饰时,默认会阻塞DOM解析和渲染。
  2. JavaScript 既可以读取和修改 DOM 属性,又可以读取和修改 CSSOM 属性。 所以,script 标签的位置很重要,在实际应用中我们通常把script标签放在body底部。

性能优化方案

1.script上加属性async,defer

对于js阻塞渲染我们可以加一些属性,改变阻塞模式

defer: 延迟执行 相当于告诉浏览器立即下载,但延迟执行。

<script src="xxx.js" defer></script>

async: 异步执行 告诉浏览器立即下载文件,与defer不同的是,标记为async的脚本并不保证按照它们的先后顺序执行。

<script src="xxx.js" async></script>

image.png

蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。

也就是说async是乱序的,而defer是顺序执行,这也就决定了async比较适用于百度分析或者谷歌分析这类不依赖其他脚本的库。

从图中可以看到一个普通的<script>标签的加载和解析都是同步的,会阻塞DOM的渲染,这也就是我们经常会把<script>写在<body>底部的原因之一,为了防止加载资源而导致的长时间的白屏,另一个原因是js可能会进行DOM操作,所以要在DOM全部渲染完后再执行。

注意: defer、async 对 inline-script 无效

2.css

<link>标签,浏览器就需要向服务器发出请求获得CSS文件,然后才继续构建DOM树和CSSOM树,可以合并所有CSS成一个文件,减少HTTP请求,减少关键资源往返加载的时间,优化渲染速度。

3.页面回流与重绘出发

回流必将引起重绘,重绘不一定会引起回流。

回流比重绘的代价要更高。所以要尽可能的避免

  • CSS

1.避免使用 table 布局。
2.尽可能在 DOM 树的最末端改变 class。
3.避免设置多层内联样式。
4.将动画效果应用到 position 属性为 absolute 或 fixed 的元素上。
5.避免使用 CSS 表达式(例如:calc())。

  • Javascript

1.避免频繁操作样式,最好一次性重写 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性。
2.避免频繁操作 DOM,创建一个 documentFragment,在它上面应用所有 DOM 操作,最后再把它添加到文档中。
3.也可以先为元素设置 display: none,操作结束后再把它显示出来。因为在 display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘。
4.避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。
5.对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。

4.其他优化方案

  • 加载部分HTML
    浏览器先加载主要HTML初始化静态部分,动态变化的HTML内容通过Ajax请求加载。这样可以减少浏览器构建DOM树的工作量,让用户感觉页面加载速度很快。

  • 压缩
    对HTML、CSS、JavaScript这些文件去除冗余字符(例如不必要的注释、空格符和换行符等),再进行压缩,减小文件数据大小,加快浏览器解析文件编码。

  • 图片加载优化
    1)小图标合并成雪碧图,进而减少img的HTTP请求次数;
    2)图片加载较多时,采用懒加载的方案,用户滚动页面可视区时再加载渲染图片。

  • HTTP缓存
    浏览器自带了HTTP缓存的功能,只需要确保每个服务器响应的头部都包含了以下的属性:
    1)ETag: ETag是一个传递验证令牌,它对资源的更新进行检查,如果资源未发生变化时不会传送任何数据。当浏览器发送一个请求时,会把ETag一起发送到服务器,服务器会根据当前资源核对令牌(ETag通常是对内容进行Hash后得出的一个指纹),如果资源未发生变化,服务器将返回304 Not Modified响应,这时浏览器不必再次下载资源,而是继续复用缓存。
    2)Cache-Control: Cache-Control定义了缓存的策略,它规定在什么条件下可以缓存响应以及可以缓存多久。
    a、no-cache: no-cache表示必须先与服务器确认返回的响应是否发生了变化,然后才能使用该响应来满足后续对同一网址的请求(每次都会根据ETag对服务器发送请求来确认变化,如果未发生变化,浏览器不会下载资源)。no-store直接禁止浏览器以及所有中间缓存存储任何版本的返回响应。简单的说,该策略会禁止任何缓存,每次发送请求时,都会完整地下载服务器的响应。
    b、public&private: 如果响应被标记为public,则即使它有关联的HTTP身份验证,甚至响应状态代码通常无法缓存,浏览器也可以缓存响应。如果响应被标记为private,那么这个响应通常只为单个用户缓存,因此不允许任何中间缓存(CDN)对其进行缓存,private一般用在缓存用户私人信息页面。
    c、max-age: max-age定义了从请求时间开始,缓存的最长时间,单位为秒。

  • HTML文档结构层次尽量少,最好不深于六层;

  • 样式结构层次尽量简单;

  • 少量首屏样式内联放在标签内;

  • 隐藏在屏幕外,或在页面滚动时,尽量停止动画;

  • 尽量缓存DOM查找,查找器尽量简洁;

  • 涉及多域名的网站,可以开启域名预解析。