浏览器渲染页面流程

想要了解浏览器的工作流程需要先了解 [ 浏览器的构造 ] 和 [ 浏览器从输入url到网页展现这过程发生了什么 ]。

浏览器用他的内核引擎来渲染页面,这个过程中也有 JavaScript 解释器、HTML 解析器(XML 解析器)、数据存储、网络服务等其他组件的配合。浏览器将 html、js、css 等文档渲染到页面过程中的步骤,叫做关键渲染路径(Critical Rendering Path),分为构建对象模型(DOM,CSSOM)、构建渲染树(RenderTree)、布局、绘制渲染树。

浏览器众多进程中,主要参与关键渲染路径的是渲染进程(Renderer Process),渲染进程里面主要包含五个线程:

  • GUI 渲染线程,用于解析 html、css、构建 DOM 树和 RenderObject 树,布局和绘制等浏览器界面渲染任务,不能和 js 引擎线程同时执行;
  • js 引擎线程,也就是我们常说的 js 内核,用于解析 javascript,运行 javascript 代码,不能和 GUI 渲染线程同时执行;
  • 事件触发线程,用于监听和触发事件,可以理解成由 js 引擎线程来执行事件绑定代码,然后另开事件触发线程用来监听事件的触发,触发以后把事件处理函数里面的代码交给 js 引擎线程来执行,但是 js 是单线程的,事件处理函数里面的代码会添加到待处理队列的末尾,等待 js 线程空闲以后执行;
  • 定时触发器线程,就是经常使用的 setInterval 与 setTimeout 所使用的线程,可以理解成由 js 引擎线程来定义一个定时器,但是定时器的计数是另开单独线程来计时并触发定时,计时时间到了以后,把要执行的回调函数里面的代码添加到待处理队列的末尾,等待 js 线程空闲以后执行;
  • http请求线程,在我们使用 XMLHttpRequest 对象发送 http 请求的时候,是新开一个线程请求,检测到对象状态改变时,如果绑定了回调函数,异步线程就产生状态变更事件,将这个回调里面的代码添加到 js 线程末尾。

构建对象模型

构建对象模型的最终结果是形成 DOM 树和 CSSOM 树,渲染引擎使用 HTML 解析器(调用 XML 解析器)解析 html(xml)文档,将各个 html(xml)元素逐个转化成 DOM 节点,从而生成 DOM 树。DOM 树构建过程:读取 html 文档,将字节转换成字符,确定 tokens(标签),再将 tokens 转换成节点,用节点构建 DOM 树。

在构建 DOM 树的同时,渲染引擎使用 CSS 解析器解析外部 css 文档以及 html(xml)元素中的样式规则(在浏览器控制台 performence 工具中 Recalculate Style 过程就是生成 CSSOM 树的过程),过程跟 DOM 树构建类似,然后生成 CSSOM 树。

在解析 html 文档时,如果碰到了 script 或者 link 标签,就会根据 src 对应的地址去加载资源,在 script 标签没有设置 async/defer 属性时,这个加载过程是下载并执行完全部的代码,此时,DOM 树还没有完全创建完毕,这个时候如果 js 企图操作 script 标签后面的 DOM 元素并修改 CSSOM,浏览器就会抛出找不到该DOM元素的错误。所以,非异步加载的 js 会阻塞 html 文档的解析和 CSSOM 的形成,想要优化解析速度,不仅仅不能让 js 阻塞 DOM 树和 CSSOM 树,还要优化 html 文件,实现 css 、html 、js 的分离。而解析 css 是在解析 html 文件下载外部 css 文件或遇到 css 内容之后才开始的,尽早的把 css 文件下载也是优化的一种手段。

构建渲染树

只要有 DOM 节点的形成,以及 CSSOM 确立,渲染树就能开始合成,并不是等 DOM 树和 CSSOM 树完全形成后才会有的,这样做会极有效率的快速显示内容到浏览器页面。渲染树是解析文档的最后结果,这一步需要明确下来文档节点需要有怎样的元素样式(默认样式或者定义好的 css 样式)。所以当 DOM 树和 CSSOM 树一出现,就有可能开始构建渲染树了,等到 JavaScript 解释器执行 js 代码,就有可能加入到渲染树的节点和样式计算。

当渲染树形成后,就能直接进行布局了吗?浏览器布局是要有节点的位置层级信息的,渲染树会再次分解为 RenderObject(渲染对象)和 RenderLayer(渲染层),RenderLayer 包含了 css 动画特效、溢出、透明、滤镜、媒体元素等等。所有的 RenderObject 和 RenderLayer 一起就决定了网页在屏幕上最终呈现出来的内容。

布局(Layout

走到这一步,浏览器就是在解析前面的内容进行重排(reflow)了,将所有相对测量值都转换成屏幕上的绝对像素,然后形成一个个的“盒子模型”。

:也有称回流,当渲染树节点发生改变,影响了节点的几何属性(如宽、高、内边距、外边距等等),导致节点位置发生变化,此时触发浏览器重排,需要重新生成渲染树。

出现浏览器重排的情况:

  • 添加或者删除可见的DOM元素;
  • 元素位置改变 —— display、float、position、overflow等等;
  • 激活CSS伪类(例如::hover);
  • 元素尺寸改变 —— 边距、填充、边框、宽度和高度;
  • 内容改变 —— 比如文本或者图片大小、内容改变而引起的计算值宽度和高度改变;
  • 页面渲染初始化(这个无法避免);
  • 浏览器窗口尺寸改变 —— resize事件发生时;
  • 操作DOM时(新增和删除可见元素);
  • 查询某些属性或调用某些方法(offset、scroll、client、getComputedStyle、focus等),这些方法不是直接引起重排,而是为了获取最新的数据强制完成了浏览器的渲染列队,间接引起了重排;

在浏览器控制台 performence 工具中,能够完整的看到由于修改 js 或者 css 而导致浏览器重排的过程:

        Recalculate Style(生成CSSOM) ==> Layout(布局) ==> Update Layer Tree(更新层树)==> Paint(绘制)==> Composite Layers(合成不同的层)

渲染

接下来就是绘制(paint)。绘制工作是由浏览器的 UI 后端组件完成的,UI 后端调用渲染器的 paint() 方法通过“光栅化”在屏幕上显示具体的像素。

光栅化

Rasterization,又称为栅格化,它用于执行绘图指令生成像素的颜色值(屏幕上的内容都是一个个像素点的颜色拼合的)。浏览器不是直接对整个图层进行光栅化,它会将图层分块,然后以块为单位进行光栅化。

与绘制对应的就是重绘(repaint),HTML 默认是流式布局的,css 和 js 会打破这种布局,改变 DOM 的外观样式以及大小和位置。这时重排和重绘就派上用场了。

重绘:屏幕的一部分要重绘。渲染树节点属性发生改变,但不影响该节点在页面当中的空间位置及大小;

出现浏览器重排的情况:

  • 修改元素 visibility、outline、背景色等不影响布局的属性;

重排与重绘区别:

  • 重排开销比重绘大;
  • 重排必定重绘,重绘不一定重排;
  • 重排要比重绘更花费时间,也更影响性能;
  • 一个元素的重排通常会带来一系列的反应,甚至触发整个文档的重排和重绘;

注意点:

  • 不要使用 table 布局;
  • 减少 NDS 查询(合并压缩 js、css、图片);
  • 使用 class 替代 DOM 样式操作;
  • css 动画操作使用 transform 替代 transition(transform 性能比 transition 好);
  • css 的深度尽量少、选择器层级尽量少,不要使用通配符(*),选择器尽量少用耗性能的后代选择器;
  • 使用 cloneNode(true or false)和 replaceChild 技术,引发一次重绘重排;
  • 合并读写 DOM 操作(通过 [FastDOM])(https://github.com/wilsonpage/fastdom) 或 虚拟 DOM 实现);
  • 可以先让元素脱离文档流(离线操作),操作完 DOM 后再带入文档流,这样只会触发一次重排(DocumentFragment 元素的应用);
  • 将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位;
  • 由于display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发两次重排;
  • 在内存中多次操作节点,完成后再添加到文档中去。例如要异步获取表格数据,渲染到页面。可以先取得数据后在内存中构建整个表格的html片段,再一次性添加到文档中去,而不是循环添加每一行;
  • 将需要更新的节点克隆,在克隆节点上进行更新操作,然后把原始节点替换为克隆节点;
  • 日常开发中多用浏览器的页面渲染工具监控重绘和重排的情况;
  • css 操作情况多参考 CSS Triggers(各个 css 属性在不同浏览器会引发的操作,紫色代表重排/回流(layout),浅绿色代表重绘(paint),深墨绿色代表合成(composite))。
CSS Triggers
CSS Triggers

调用硬件(GPU)加速(原理是将元素提升到单独的合成层,跳过重绘重排,不过会加大内存消耗,性能不好的电脑会页面卡顿,不能盲目的使用合成层):

  • 使用具有 3D transform 或透视变换的 CSS 属性;
  • 使用 video 元素;
  • 覆盖在 video 元素上的视频控制栏;
  • backface-visibility 为 hidden;
  • 使用 2D 加速或 3D 的 canvas 元素(普通 2D 的 canvas 不会);
  • 使用硬件加速的插件,比如 Flash 插件;
  • 对 opacity 加 CSS 动画或使用一个动画 webkit 变换的元素;
  • 对 opacity、transform、fliter、backdropfilter 应用了 animation 或者 transition(需要是 active 的 animation 或者 transition,当 animation 或者 transition 效果未开始或结束后,提升合成层也会失效);
  • 图层使用加速的 CSS 过滤器;
  • css will-change 设置为 opacity、transform、top、left、bottom、right(其中 top、left 等需要设置明确的定位属性,如 relative 等);
  • 在 DPI 较高的屏幕上,fix 定位的元素会自动地被提升到合成层中;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
浏览器渲染机制可以分为以下几个步骤: 1. 构建 DOM 树:根据 HTML 文本构建 DOM 树,即将 HTML 文本解析成一个个的节点,然后将这些节点按照其在文档中的层级关系组成一棵树。 2. 构建 CSSOM 树:根据 CSS 文本构建 CSSOM 树,即将 CSS 文本解析成一个个的样式规则,然后将这些样式规则按照其在文档中的层级关系组成一棵树。 3. 合并生成渲染树:将 DOM 树和 CSSOM 树组合成渲染树(也称为呈现树或布局树)。渲染树只包含需要显示的节点和这些节点的样式信息。渲染树的构建过程中,浏览器会忽略掉那些不需要显示的节点,例如 head 标签、display:none 的节点等。 4. 计算布局信息:根据渲染树计算每个节点在屏幕上的位置和大小,这个过程被称为布局或排版(layout 或 reflow)。布局是一个相当昂贵的操作,因为浏览器需要遍历渲染树的每个节点,并根据节点的样式、大小、位置等信息计算其在屏幕上的位置和大小。 5. 绘制页面:根据渲染树和布局信息,将页面绘制到屏幕上,这个过程被称为绘制(painting 或 rasterization)。绘制的过程中,浏览器会将渲染树中每个节点的内容转换成位图,然后将这些位图组合成一张完整的页面。 6. 提交更新:将绘制好的页面提交到 GPU,由 GPU 将其显示到屏幕上。 以上是浏览器渲染机制的主要流程,其中布局(layout 或 reflow)是整个渲染过程中最耗时的步骤,因此在开发中需要尽可能减少布局的次数,以提高页面的性能表现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值