关于浏览器渲染原理流程

1、一个经典的问题:向浏览器输入URL地址,发生了什么

而这部分也只是在说浏览器获取数据的部分(也就是页面加载的过程),至于浏览器拿到数据后是如何渲染的呢(不同浏览器渲染引擎不相同,这里以Webkit为例)

2、浏览器渲染过程

流程图如下:

  1. 解析HTML,生成DOM树;解析CSS,生成CSSOM树

  2. 将DOM树和CSSOM树结合,生成渲染树Render Tree

  3. Layout回流:根据生成的渲染树,进行回流,得到节点的几何信息(位置、大小)

  4. Painting重绘:根据渲染树以及回流得到的几何信息,得到节点的绝对像素

  5. Display:将像素发送给GPU,最后通过调用操作系统Native GUI的API绘制,展示在页面上

2.1 构建DOM树的过程

  • 【字节数据】:网络中传输的内容其实是0和1这种字节数据,浏览器从磁盘或网络读取HTML的字节数据
  • 【字符串】: 浏览器并根据文件的指定编码(如UTF-8)将它们转换为字符串
  • 【Token】: 浏览器将字符串转换为W3CHTML5标准规定的各种令牌Token,例如、等其他尖括号内的字符串,Token中会标识出当前Token是'开始标签'还是结束标签'还是'文本'等信息,每个令牌都具有特殊含义和一组规则。
  • 【Node】:紧接着这些标记会被转换为Node。事实上,不是等所有Token都转换完再去生成节点对象,而是一边生成Token一边消耗Token来生成节点对象。(带有结束标签标识的Token不会创建节点对象)
  • 【DOM】:这些Node会根据不同Node之间的联系生成DOM树🌲

举例:HTML文本:

这段HTML就会解析成:

2.2 构建CSSOM的过程

这个过程与构建DOM的过程很相似,当浏览器接收到一段CSS,浏览器首先要做的就是识别出Token,然后构建节点并生成CSSOM。

浏览器会根据CSS规范来解析CSS,与DOM不同的是CSS会向下层叠,即子节点可能会继承父节点的一些属性。浏览器解析CSS过程是阻塞的,需要解析完所有的CSS才会使用CSS样式

CSS解析可以与DOM解析同时进行,如果只有CSS和HTML的页面,CSS不会影响DOM树的创建,但是页面中还有JS的话,就不一样了

  • 第一种情况:当前页面中只有HTML和JS,且JS非外部引入

先去执行JS的原因是:JS可能会操作到当前已经生成的DOM节点

  • 第二种情况:当前页面中同时有HTML、CSS、JS,且都非外部引入

因为JS可能会操作CSSOM节点

  • 第三种情况:当前页面中只有HTML和JS,且JS非外部引入

Webkit渲染引擎有一个优化,当渲染进程接收HTML文件字节流时,会先开启一个预解析线程,如果遇到JS文件或者CSS文件,那么预解析线程会提前下载这些数据

2.3 构建渲染树

通过DOM树和CSSOM树,浏览器就可以通过二者构建渲染树了。浏览器会先从DOM树的根节点开始遍历每个可见节点,然后对每个可见节点找到适配的CSS样式规则并应用。

  • DOM树与Render树不是一一对应的

  • visibility:hidden和display:none是不一样的,前者隐藏元素,但元素仍占据着布局空间(即渲染成一个空框),而后者将元素从渲染树中完全移除,元素既不可见,也不是布局的组成部分

样式计算是个很复杂的问题,DOM中的一个元素对应样式表中的多个元素。样式表包括了所有样式:浏览器默认样式表、自定义样式表、inline样式表。

2.4 布局Layout

创建渲染树后,下一步就是布局(或者是回流),布局流程的输出是一个盒模型,会精确地捕获每个元素在视口内的确切位置和尺寸,所有相对测量值都将转换为屏幕上的绝对像素。将每个元素安置在浏览器窗口的正确位置。

而有时候我们在文档布局完成后对DOM进行修改,这时候可能会引发重新布局。

对渲染树的布局可分为全局和局部的,全局即对整个渲染树进行重新布局,比如当改变了窗口尺寸或方向或修改了根元素的尺寸或字体大小;而局部可以是对某部分或某一个渲染对象进行重新布局

布局是一个从上到下,从外到内进行的递归过程,从根渲染对象,即对应着HTML文档根元素,然后下一级渲染对象,即对应着元素,如此层层递归,依次计算每一个渲染对象的几何信息

每一个渲染对象的布局流程:

2.5 绘制Painting

在绘制阶段,系统会遍历渲染呈现树,并调用呈现器的Paint方法,将呈现器的内容显示在屏幕上。(我们通过构造渲染树和回流阶段,我们知道了哪些节点是可见的,以及可见节点的样式和具体的几何信息(位置、大小),那么我们就可以将渲染树的每个节点都转换为屏幕上的实际像素)

3、回流重绘

回流这一阶段主要是计算节点的位置和几何信息,那么当页面布局和几何信息发生变化的时候,就需要回流

何时会触发回流呢:

  • 页面一开始渲染的时候
  • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(包括外边距、内边距、边框大小、高度和宽度等)
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片替代
  • 元素字体大小变化
  • 激活CSS伪类(如:hover)
  • display:none触发回流,visibility:hidden只会触发重绘

页面中元素样式改变但不影响在文档的位置时,不触发回流,只触发重绘。(回流一定会触发重绘,而重绘不一定会回流)

回流的成本要比重绘要更高,如何进行优化,减少回流和重绘呢:

  • 样式结构层级尽量简单,CSS选择符从右往左匹配查找
  • 在脚本中尽量减少DOM操作,尽量缓存访问DOM的样式信息
  • 减少通过JS代码修改元素样式,尽量使用修改class名方式操作样式或动画
  • 使用transform代替top
  • 使用visibility代替display:none
  • script标签的使用加defer或async属性(defer:表示js文件会并行下载,但是会放到HTML解析完成后顺序执行;async:表示JS文件下载和解析不会阻塞渲染)
  • 不要使用table布局(小改动可能造成整个table重新布局)

最后

来自一位小白的梳理 如果有什么问题欢迎评论区沟通~

相关推荐
黄尚圈圈24 分钟前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水1 小时前
简洁之道 - React Hook Form
前端
正小安3 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch5 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光5 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   5 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   5 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web5 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常5 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇6 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器