为什么每个前端开发者都要懂网页渲染?
定义页面布局时,应该从一开始就优化呈现。样式和脚本在页面渲染中起着非常重要的作用。专业人士知道一些技巧来避免一些性能问题。
本文不会深究浏览器的技术细节,而是提供一些通用原则。不同的浏览器引擎有不同的工作原理,这使得特定浏览器的学习更加复杂。
浏览器如何渲染页面?
先说浏览器渲染页面的一般流程:
由从服务器接收的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/