为什么每个前端开发者都要懂网页渲染?

今天我将重点讨论网页渲染及其重要性。虽然很多文章都提到了这个话题,但是大部分信息都是零碎的。为了思考这件事,我需要研究许多信息来源。所以我觉得我应该写这篇文章。我相信这篇文章对初学者会很有用,对想刷新巩固已有知识的专家也适用。

定义页面布局时,应该从一开始就优化呈现。样式和脚本在页面渲染中起着非常重要的作用。专业人士知道一些技巧来避免一些性能问题。

本文不会深究浏览器的技术细节,而是提供一些通用原则。不同的浏览器引擎有不同的工作原理,这使得特定浏览器的学习更加复杂。

浏览器如何渲染页面?

先说浏览器渲染页面的一般流程:

由从服务器接收的HTML形成?文档对象模型

加载并解析样式以形成CSS对象模型。

然后DOM和CSSOM创建渲染树,渲染树是渲染对象的集合(Webkit分别称之为“渲染器”和“渲染对象”,Gecko engine称之为“框架”)。除了不可见的元素(比如head标签和一些带有display:none属性的元素),渲染树映射DOM的结构。在渲染树中,每个文本字符串都被视为独立的渲染器。每个渲染对象都包含其对应的具有计算样式的DOM对象(或文本块)。换句话说,渲染树描述了DOM的直观表示。

对于每一个渲染元素,都会计算出它的坐标,这就是所谓的“布局”。浏览器使用一种只需要处理一次的“流方法”来布局所有元素(表格需要处理多次)。

最后,布局显示在浏览器窗口中。这个过程叫做“画”。

重画

当你在页面上修改一些不需要改变定位的样式(比如背景色、边框色、可见性),浏览器只会将新的样式重绘到元素上(这叫做“重绘”或者“重定义样式”一次)。

重新整理

当页面上的更改影响到文档的内容、结构或元素定位时,就会发生重组(或“重新布局”)。重排通常由以下变化触发:

DOM操作(如添加、删除、更改或改变元素顺序)。

?内容的变化,包括表格中文字的变化。

?计算或更改CSS属性。

?添加或删除样式表。

?更改“类”属性。

?浏览器窗口的操作(调整大小、滚动窗口)。

?激活伪类(如悬停状态)。

浏览器如何优化渲染?

浏览器尽最大努力将重新排列过程限制为仅覆盖已更改元素的区域。例如,位置为absolue或fixed的元素的大小变化只影响其自身及其后代,而对位置为static的元素进行同样的操作将导致其后面的所有元素重新排列。

还有一个优化就是在运行一段Jjavascript代码的时候,浏览器会缓存一些修改,然后在执行代码的时候,一次性执行这些修改。

例如,以下代码触发重绘和重新排列:

var?$body?=?$(' body ');?

$body.css('padding ',?1px’);?//?重新排列?重新粉刷?

$body.css('color ',?红色’);?//?重新粉刷?

$body.css('margin ',?2px’);?//?重新排列?重新粉刷?

//?事实上,只执行一次重新排列和重新绘制。?

如上所述,访问一个元素的属性将被强制重新排列。如果我们在上面的代码中添加一行代码来读取元素的属性,就会出现这种情况:

var?$body?=?$(' body ');?

$body.css('padding ',?1px’);?

$ body . CSS(' padding ');?//?在这里,元素的属性被读取一次,并且会发生强制重排。

$body.css('color ',?红色’);?

$body.css('margin ',?2px’);?

由于上面的代码,有两个重排。因此,为了提高性能,您应该说读取元素属性的代码是组织在一起的(详细示例请参见JSBin上的代码)。

在一种情况下,必须触发强制重排。比如对一个元素的同一个属性修改两次(比如margin-left),初始设置100px不做动画,然后通过动画把值修改为50px。具体的例子可以看,当然我这里会讲更多的细节。

让我们从一个带有过渡的CSS类开始:

。has-过渡?{ ?

-WebKit-过渡:?左边距?1s?放松;

-moz-转场:?左边距?1s?放松;

-o-转场:?左边距?1s?放松;

过渡:?左边距?1s?放松;?

}?

然后实施:

//我们的元素默认有“has-transition”属性?

var?$targetElem?=?$(' # target elemid ');?

//删除包含transition的类?

$ target elem . remove class(' has-transition ');

//?当包含transition的类没有了,改变元素属性?

$targetElem.css('margin-left ',?100);?

//?然后添加回包含转换的类。

$ target elem . add class(' has-transition ');?

//?更改元素属性?

$targetElem.css('margin-left ',?50);?

上述实现没有按预期运行。所有的修改都被浏览器缓存,只会在上面代码的最后执行。我们需要的是强制重排,这可以通过进行以下修改来实现:

//删除包含transition的类?

$(这个)。remove class(' has-transition ');?

//?更改元素属性?

$(这个)。css('左边距',?100);?

//触发强制重排,使改变后的类或属性可以立即执行。?

$(this)[0]。偏高;?//?OffsetHeight只是一个例子,其他属性也可以。?

//?然后添加回包含转换的类。

$(这个)。add class(“has-transition”);?

//?更改元素属性?

$(这个)。css('左边距',?50);?

现在,这段代码按照我们的预期运行。

实用的优化建议

总结了一些有用的信息,我建议如下:

?创建合法的HTML和CSS,不要忘记进行文件编码。样式应该写在head标签中,脚本标签应该加载在body标签的末尾。

?尽量简化优化CSS选择器(这个优化点被大部分使用CSS预处理器的开发者忽略了)。保持嵌套层数最少。以下是CSS选择器的性能排名(从最快开始):

ID选择器:#id

类选择器:?。班级

标签:?差异

相邻的兄弟元素:a+i

父元素选择器:?ul & gt里

通配符选择器:?*

伪类和伪元素:?答:悬停?你应该记得浏览器从右向左处理选择器,这就是为什么最右边的选择器更快-# id或。班级。

div?*?{...}?//?不好?

。列表?李?{...}?//?不好?

。列表项目?{...}?//?好吗?

#列表?。列表项目?{...}?//?好吗?

在脚本中,尽量减少DOM操作。缓存所有内容,包括属性和对象(如果可以重用的话)。在执行复杂操作时,最好操作一个“脱机”元素(“脱机”元素是指只存在于内存中,与DOM对象分离的元素),然后将这个元素插入DOM。

如果使用jQuery,请遵循jQuery选择器最佳实践。

要改变元素的样式,修改“class”属性是最有效的方法之一。DOM树的层次越深,效率就越高(这也有助于将表示从逻辑中分离出来)。

尽可能仅动画显示位置绝对或固定的元素。

禁用一些复杂的滚动时?:悬停?动画是一个好主意(例如,向body标签添加一个非悬停类)。

有关更多详细信息,您可以阅读以下文章:

1.浏览器如何工作

2.渲染:重画、重排/重新布局、重新设计样式

希望这篇文章能帮到你!

原文链接:?frontendbabel?翻译:?伯乐在线?-?莫伊泽

翻译链接:?/72692/