欢迎光临搜索优化网站,为您在线提供搜索引擎优化问题!

搜索优化

我们提供一站式关键词优化快速稳定,解决网站排名查询困难。

浏览器渲染优化

作者:jcmp      发布时间:2021-04-21      浏览量:0
背景(做前端永远也跨不过去了就是性能优化

背景(做前端永远也跨不过去了就是性能优化)

公司目前有大量前后端耦合项目,浏览器的首屏加载速度非常的慢,在不段优化中学到了非常多的东西,在此记录总结一下。

首先认识几个名词这在chrome devtools 十分常见也是你优化的一些数据参考。

FP(First Paint):首次绘制,标记浏览器渲染任何在视觉上不同于导航前屏幕内容的时间点 FCP(First Contentful Paint):首次内容绘制,标记的是浏览器渲染第一针内容 DOM 的时间点,该内容可能是文本、图像、SVG 或者 等元素 FMP(First Meaning Paint):首次有效绘制,标记主角元素渲染完成的时间点,主角元素可以是视频网站的视频控件,内容网站的页面框架也可以是资源网站的头图等 TTFB (Time to First Byte) : 指的是浏览器开始收到服务器响应数据的时间(后台处理时间+重定向时间)。

记得我们前端的面试题里有一道题 在浏览器里输入域名后浏览器都干了什么?

在TTFB之前的工作我们今天先不谈,因为这个时间更偏后端一点,并且pc和移动端采取的方案也不尽相同。

浏览器渲染过程

浏览器渲染过程大体分为如下三部分:

1)浏览器会解析三个东西:

2)解析完成后,浏览器引擎会通过DOM Tree 和 CSS Rule Tree 来构造 Rendering Tree。

3)最后通过调用操作系统Native GUI的API绘制。

构建DOM

浏览器会遵守一套步骤将HTML 文件转换为 DOM 树。宏观上,可以分为几个步骤:

在网络中传输的内容其实都是 0 和 1 这些字节数据。当浏览器接收到这些字节数据以后,它会将这些字节数据转换为字符串,也就是我们写的代码。

这时候你一定会有疑问,节点与节点之间的关系如何维护?

事实上,这就是Token要标识“起始标签”和“结束标签”等标识的作用。例如“title”Token的起始标签和结束标签之间的节点肯定是属于“head”的子节点。

上图给出了节点之间的关系,例如:“Hello”Token位于“title”开始标签与“title”结束标签之间,表明“Hello”Token是“title”Token的子节点。同理“title”Token是“head”Token的子节点。

事实上,构建DOM的过程中,不是等所有Token都转换完成后再去生成节点对象,而是一边生成Token一边消耗Token来生成节点对象。换句话说,每个Token被生成后,会立刻消耗这个Token创建出节点对象。 注意:****带有结束标签标识的Token不会创建节点对象。

接下来我们举个例子,假设有段HTML文本:

Web page parsing

Web page parsing

This is an example Web page.

上面这段HTML会解析成这样:

构建CSSOM

DOM会捕获页面的内容,但浏览器还需要知道页面如何展示,所以需要构建CSSOM。

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

在这一过程中,浏览器会确定下每一个节点的样式到底是什么,并且这一过程其实是很消耗资源的。因为样式你可以自行设置给某个节点,也可以通过继承获得。在这一过程中,浏览器得递归 CSSOM 树,然后确定具体的元素到底是什么样式。

注意:****CSS匹配HTML元素是一个相当复杂和有性能问题的事情。所以,DOM树要小,CSS尽量用id和class,千万不要过渡层叠下去 。

构建渲染树

当我们生成 DOM 树和 CSSOM 树以后,就需要将这两棵树组合为渲染树。

在这一过程中,不是简单的将两者合并就行了。 渲染树只会包括需要显示的节点和这些节点的样式信息 ,如果某个节点是 display:none 的,那么就不会在渲染树中显示。

我们或许有个疑惑: 浏览器如果渲染过程中遇到JS文件怎么处理 ?

渲染过程中,如果遇到

没有 defer 或 async,浏览器会立即加载并执行指定的脚本,也就是说不等待后续载入的文档元素,读到就加载并执行。

2)情况2 ( 异步下载 )。

async 属性表示异步执行引入的 JavaScript,与 defer 的区别在于,如果已经加载好,就会开始执行——无论此刻是 HTML 解析阶段还是 DOMContentLoaded 触发之后。需要注意的是,这种方式加载的 JavaScript 依然会阻塞 load 事件。换句话说,async-script 可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行。

3)情况3 ( 延迟执行 )。

defer 属性表示延迟执行引入的 JavaScript,即这段 JavaScript 加载时 HTML 并未停止解析,这两个过程是并行的。整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,然后触发 DOMContentLoaded 事件。

defer 与相比普通 script,有两点区别: 载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成之后;****在加载多个JS脚本的时候,async是无顺序的加载,而defer是有顺序的加载。

2.为什么操作 DOM 慢

把 DOM 和 JavaScript 各自想象成一个岛屿,它们之间用收费桥梁连接。——《高性能 JavaScript》

JS 是很快的,在 JS 中修改 DOM 对象也是很快的。在JS的世界里,一切是简单的、迅速的。但 DOM 操作并非 JS 一个人的独舞,而是两个模块之间的协作。

因为 DOM 是属于渲染引擎中的东西,而 JS 又是 JS 引擎中的东西。当我们用 JS 去操作 DOM 时,本质上是 JS 引擎和渲染引擎之间进行了“跨界交流”。这个“跨界交流”的实现并不简单,它依赖了桥接接口作为“桥梁”(如下图)。

过“桥”要收费——这个开销本身就是不可忽略的。我们每操作一次 DOM(不管是为了修改还是仅仅为了访问其值),都要过一次“桥”。过“桥”的次数一多,就会产生比较明显的性能问题。因此“减少 DOM 操作”的建议,并非空穴来风。

3.你真的了解回流和重绘吗

渲染的流程基本上是这样(如下图黄色的四个步骤):1.计算CSS样式 2.构建Render Tree 3.Layout – 定位坐标和大小 4.正式开画。

注意:上图流程中有很多连接线,这表示了Javascript动态修改了DOM属性或是CSS属性会导致重新Layout,但有些改变不会重新Layout,就是上图中那些指到天上的箭头,比如修改后的CSS rule没有被匹配到元素。

这里重要要说两个概念,一个是Reflow,另一个是Repaint。

我们知道,当网页生成的时候,至少会渲染一次。在用户访问的过程中,还会不断重新渲染。重新渲染会重复回流+重绘或者只有重绘。 回流必定会发生重绘,重绘不一定会引发回流 。重绘和回流会在我们设置节点样式时频繁出现,同时也会很大程度上影响性能。回流所需的成本比重绘高的多,改变父节点里的子节点很可能会导致父节点的一系列回流。

1)常见引起回流属性和方法

任何会改变元素几何信息(元素的位置和尺寸大小)的操作,都会触发回流,

2)常见引起重绘属性和方法

3)如何减少回流、重绘

for(let i = 0; i < 1000; i++) { // 获取 offsetTop 会导致回流,因为需要去获取正确的值 console.log(document.querySelector('.test').style.offsetTop) }

性能优化策略

基于上面介绍的浏览器渲染原理,DOM 和 CSSOM 结构构建顺序,初始化可以对页面渲染做些优化,提升页面性能。

总结

综上所述,我们得出这样的结论: