Skip to content

浏览器之资源解析渲染 #20

Open
@myLightLin

Description

@myLightLin

前言

我们日常用到的浏览器,主要功能就是通过用户输入的网址,然后去请求服务器,拿到资源后解析呈现在我们眼前。这部分涉及很多知识,比如网络 DNS 解析,HTTP 请求,TCP 握手,TLS 握手等等。今天这篇文章不讲网络部分的内容,主要讲浏览器从服务器拿到资源后,怎么进行解析渲染的过程。

主流浏览器的架构

对于浏览器的功能界面,市场上各家浏览器基本都大同小异,主要由以下几个模块组成:

  • 用户界面前端:包含地址栏,前进/后退按钮,书签栏等
  • 浏览器引擎:在 UI 和渲染引擎之间执行操作
  • 渲染引擎:负责界面的渲染展示,比如我们输入网站看到的页面,就是由渲染引擎渲染负责执行的,不同的浏览器使用的渲染引擎有所区别,IE 用 Trident,Firefox 用 Gecko,Safari 用 WebKit,Chrome 跟 Opera 用的是 Blink,这个 Blink 是由 WebKit 分化而来
  • 网络模块:主要负责 http 等网络请求的处理
  • 用户界面后端:用于绘制一些窗口组件之类
  • JS 解析器:执行 JS 代码
  • 数据存储:我们所熟悉的 cookie,localStorage , sessionStorage,IndexDB 的存放地方

这里借用一张来自 web.dev/howbrowserswork 网站的图:
image

了解了浏览器的主要架构后,我们今天要说的浏览器解析资源渲染就是 渲染引擎 干主要的活,其它引擎协助配合。

资源的解析渲染过程

以 Chrome 浏览器为例,借用一张经典的图:
image

整个过程分为:

  • 解析 HTML,构建 DOM 树
  • 解析 CSS,构建 CSS OM 树
  • 合并生成渲染树,进行布局
  • 接着进行绘制
  • 最后呈现

浏览器渲染的过程是 渐进式 的,而不是等文档解析完再呈现,是一边解析一边渲染,为了尽快让用户看到页面。

解析 HTML

这个过程会获取 HTML 文档,然后进行 词法分析(token 标记化) —— 语法分析 —— 生成解析树(有自上而下和自下而上两种算法),接下来将解析树进行翻译,翻译的目标是生成机器码。在解析的过程中,会对标签进行纠错,比如你写了个开始标签,但是漏了结束标签,算法会尝试自动修正。当解析完毕后,document 文档的状态就是 complete,此时就会触发 load 事件。

在解析的时候还会遇到 script 标签,这时会停止解析转而去执行 JS 脚本,如果脚本是外链的,就会去发起网络请求获取内容。由于 JS 会阻塞解析,因此一般推荐将 <script> 写在文档的最底部,或者对 script 脚本设置 asyncdefer 属性。async 属性表示另开一个线程去异步下载脚本,这个时候 HTML 是继续解析的,然后下载完后,HTML 就会停止解析,开始执行 script 脚本,如果有多个 async 的 script 的话,它不保证脚本的执行顺序;而 defer 也会在解析的时候去下载脚本,但它会延迟到 HTML 解析完才执行脚本,它可以保证脚本的执行顺序。
下面这张是 stackoverflow 上一张经典的解释图:
image

解析 CSS

CSS 的解析是根据特定语法规范来做的,主要处理一些选择器、标识符之类。引擎会按照一套预定的规则,对文档里的 stylesheet 进行遍历,然后根据级联规则,确定样式的优先级,最后计算每个元素的最终样式信息,形成 CSS OM 树。

构建渲染树

渲染树就是 DOM 树加上 CSS 规则应用后,最终生成的一颗树,比如在这个过程中。display: none 的元素就不会出现在渲染树了。

布局

这个过程主要是通过渲染树中的信息,以递归的形式计算出每个节点的尺寸大小和在页面中的具体位置。

绘制

浏览器将渲染树中的节点转换成在屏幕上绘制的指令,然后所有层按照一定顺序合并为一个图层并绘制在屏幕上

重排与重绘

上面的布局和绘制过程就涉及到我们经常说的重排重绘。如果页面频繁地进行重排,会导致很大的性能花销,因此平时写代码的时候应注意尽量减少重排重绘。

哪些操作会造成重排?

  • 页面初始渲染的时候,会重排一次
  • 增加修改 DOM 元素
  • 更改元素的 widthheightfont-familyfont-size
  • 更改元素的位置
  • 缩放浏览器窗口
  • CSS 伪类(:hover)
  • display: none 元素隐藏
  • 获取布局信息时,如 offsetWidth 和 offsetHeight 的查询

哪些操作会造成重绘?

  • 修改元素的外观,比如 color,opacity,outline 等
  • visibility: hidden
  • transform: translate

如何减少重绘、重排?

  • 对 DOM 做离线修改,批量写入和读取
  • 对 DOM 节点的引用要存到变量里,不能在 for 循环里频繁获取执行
  • 对样式的修改,应该使用添加 class 类名的方式,而不是直接用 JS 动态修改属性值
  • 合理使用特殊样式属性,如 will-change ,将渲染层提升为合成层,开启 GPU 加速,提高页面性能
  • 划分好 HTML 结构,尽可能修改小范围的 DOM 层级
  • 不要使用 table 布局,随意改变可能就会引起重排

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions