阅读 259

浏览器的渲染机制

1. 前言

  1. 在浏览器地址中从输入url地址到页面显示出来,这个过程发生了什么?
  2. 浏览器拿到页面资源后是怎么加载的?
  3. 浏览器又是怎么渲染页面的?
  4. ...

2. 从资源加载到浏览器解析的过程

当我们在浏览器输入一个url后,浏览器会进行一系列的操作:

  1. 浏览器查看缓存,如果资源存在缓存,直接加载。如果资源未缓存,则发起新的请求
  2. 在发起新的请求前,会进行DNS解析(也就是把输入的url解析成真实的IP地址)
  3. 创建TCP连接(三次握手的过程)
  4. TCP连接创建成功后,发起http请求
  5. 服务器处理请求并返回对应的资源文件
  6. 浏览器解析渲染页面

3. 浏览器加载资源

客户端从服务器获取到需要渲染页面的源代码后,会开启一个GUI渲染线程,自上而下的解析代码,最后绘制出对应的页面。

这个自上而下的解析代码的过程是同步的,但是有一些操作也是异步的

  1. 关于css资源的加载
    • 遇到 style 标签:这是同步的,直接交给GUI渲染线程解析
    • 遇到 link 外链样式这是异步的,会开启一个http网络请求线程去请求资源文件,同时GUI渲染线程继续向下渲染。当GUI渲染线程同步操作都处理完成后,再把请求回来的资源文件进行解析渲染
    • 遇到 @import 导入样式这是同步的,会开启一个http网络请求线程去请求资源文件,但是在资源文件没有请求回来之前,此时的GUI渲染线程是被阻塞的,不会继续向下解析和渲染
  2. 关于js资源的加载
    • 默认都是同步的,必须基于http网络线程把js文件请求回来,并且交给 “S引擎(渲染)线程”解析完成后,GUI渲染线程才会继续向下渲染。所以才尽可能把js文件写在页面底部
    • script的async属性:此时文件的加载是异步的,不会阻止GUI线程。但是一旦资源请求回来后,会中断GUI线程,先把请求回来的js文件进行解析渲染
    • script的defer属性:此时文件的加载是异步的,不会阻止GUI线程。与async不同,defer不会中断GUI线程的渲染,而是在GUI渲染完成后,再把请求回来的js文件进行解析渲染
  3. 关于img或是音视频资源的加载
    • 基于http网络线程把资源请求回来,不会阻塞GUI线程。是在GUI渲染完成后,再把请求回来资源进行渲染

4. 浏览器渲染机制

4.1 渲染引擎的一般渲染过程

9-1.png

  1. HTML解析器:解析HTML文本,浏览器会把HTML结构字符串解析转换DOM树形结构

html.png

  1. CSS解析器:渲染样式代码,生成CSSOM Tree

css.png

  1. Javascript引擎:等到Javascript 脚本文件加载后, 通过 DOM API 来操作 DOM Tree

js.png

4.2 页面渲染的过程

clipboard.png

页面渲染的步骤:

  1. DOM Tree:自上而下渲染完页面,整理好整个页面的DOM结构关系
  2. CSSOM Tree:按照引入的CSS顺序,依次渲染样式代码,生成样式树
  3. RENDER Tree:把生成的DOM树和CSSOM树合并在一起
  • Render Tree和DOM Tree是不完全对应
  • display: none的元素不在Render Tree中
  • visibility: hidden的元素在Render Tree中
  1. Layout布局:从Render Tree的根节点开始遍历,由于渲染树的每个节点都是一个Render Object对象,包含宽高,位置,背景色等样式信息。所以浏览器就可以通过这些样式信息来确定每个节点对象在页面上的确切大小和位置
  2. 分层处理:按照层级定位分层处理(z-index的层级关系)
  3. Painting:将渲染树绘制成像素,绘制所需的时间跟CSS样式的复杂度成正比,绘制完成后,用户> 就可以看到页面的最终呈现效果了

5. 重绘和回流

  1. 重绘:元素样式的改变(但宽高,大小,位置等不变)
    • 如:color,background-color等
  2. 回流:元素的大小或位置发生变化(当页面布局和几何信息发生变化的时),触发重新布局,导致Render Tree重新计算布局和渲染
    • 如:增删DOM节点,浏览器窗口尺寸发生变化,元素尺寸,内容发生变化等等

回流一定会触发重绘,重绘不一定会回流

5.1 如何减少重绘和回流

  1. 样式集中修改:如dom.style.cssText = 'width: 100; height: 200px;'
  2. 减少直接操作 DOM,使用现代流行的框架等

6. 性能优化

基于DOM 和 CSSOM 结构构建顺序,以及文件加载的过程,可以对页面渲染做些优化,提升页面性能:

  1. 针对 DOM 的优化:
    • 优化 DOM 结构,减少 DOM 的层级嵌套
    • 不要使用非标准的标签等
  2. 针对 css 的优化:
    • 尽可能不要使用 @import(阻塞GUI渲染)
    • css 选择器链短一些
    • link 引入css 放在head中
    • link 标签的 rel属性 中的属性值设置为 preload 能够让你在你的HTML页面中可以指明哪些资源是在页面加载完成后即刻需要的,最优的配置加载顺序,提高渲染性能
    • ...
  3. 其他资源的优化
    • 对于 script 标签,尽可能放在页面底部(防止其阻塞GUI渲染),对于部分的 script 需要使用async 和 defer
    • 对于 img ,使用懒加载
  4. 减少 DOM 回流和重绘
  5. ...

7. 参考文章

文章分类
前端
文章标签