浏览器从右往左(也称为自底向上)解析 CSS 选择器,这样的匹配节点的方式可以快速、准确的与 render 树上的节点进行匹配,避免了许多无效匹配。浏览器需要评估的规则越少,样式引擎执行的速度就越快。
例如:
.menu ul li a {color: plum;}
浏览器会先检查 a
、li
、ul
,然后是 .menu
。
这是因为当浏览器扫描页面时,它只需要查看当前的节点和之前扫描过的所有节点。
需要注意的是,浏览器在获得完整的节点时就开始处理,无需等待整个页面,除非它找到一个脚本,在这种情况下,它会暂时暂停并完成脚本的执行,然后继续执行。
如果是相反的方式,则效率会很低,因为浏览器在第一次检查时找到了它正在扫描的元素,但随后被迫继续在文档中查找所有其他选择器。为此,浏览器需要有整个 html,并可能需要扫描整个网页之前,它才开始 css 绘制。
这与大多数 lib 解析 DOM 的方式相反。DOM 就是在这里构建的,它不需要扫描整个页面,只需找到第一个元素,然后继续匹配其中的其他元素。
详细内容你可以在 Why do browsers match CSS selectors from right to left? 查看,进行讨论。
<style>
标签包含的内容以及 DOM 元素的 style
属性内嵌的 CSS。这其实也就是内联、行内和外部引入样式的三种方式。StyleSheet object
),可以通过 document.styleSheets
获取。这个对象包含了带有选择器的 CSS 规则,和对应 CSS 语法的对象(选择器、属性和属性各自值的数据结构)译自 HTML parsing。
浏览器渲染引擎从网络层取得请求的文档,一般情况下文档会分成 8KB 大小的分块传输。
HTML 解析器的主要工作是对 HTML 文档进行解析,生成解析树。
解析树是以 DOM 元素以及属性为节点的树。DOM 是**文档对象模型(Document Object Model)**的缩写,它是 HTML 文档的对象表示,同时也是 HTML 元素面向外部(如 JavaScript)的接口。树的根部是 Document 对象。整个 DOM 和 HTML 文档几乎是一对一的关系。
解析算法:
HTML 不能使用常见的自顶向下或自底向上方法来进行分析。主要原因有以下几点:
document.write()
方法会在源码中添加内容,也就是说,解析过程实际上会改变输入的内容由于不能使用常用的解析技术,浏览器创造了专门用于解析 HTML 的解析器。解析算法在 HTML5 标准规范中有详细介绍,算法主要包含了两个阶段:标记化(tokenization)和树的构建。
解析结束之后:
浏览器开始加载网页的外部资源(CSS,图像,JavaScript 文件等)。
此时浏览器把文档标记为可交互的(interactive),浏览器开始解析处于推迟(deferred)模式的脚本,也就是那些需要在文档解析完毕之后再执行的脚本。之后文档的状态会变为完成(complete),浏览器会触发**加载(load)**事件。
注意解析 HTML 网页时永远不会出现**无效语法(Invalid Syntax)**错误,浏览器会修复所有错误内容,然后继续解析。
同源策略是一个重要的安全策略,它用于限制一个 origin 的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。
跨域其实就是同源策略这种限制引起的。
那么怎么样才算是同源呢?
同源是指协议 + 域名(主域名、子域名)+ 端口三者相同,即便两个不同的域名指向同一个 IP 地址,也非同源。
下表给出了相对 http://store.company.com/dir/page.html
同源检测的示例:
浏览器中的大部分内容都是受同源策略限制的,但是以下三个标签可以不受限制:
<img src=xxx>
<link href=xxx>
<script src=xxx>
这就有了第一种解决跨域限制的方法:JSONP。它便是利用了不受同源策略限制的机制,可以在不同源的情况下请求资源。(这里不过多介绍,后面有资源链接自己看)
但要注意,普通的脚本和加上 type="module"
的脚本对 CORS(跨源资源共享)的处理方式不同。
<!-- 不是 CORS 的请求 --><script src="https://example.com/script.js"></script><!-- CORS 请求 --><script type="module" src="https://example.com/script.js"></script>
如果你在模块上下文中请求 JavaScript 文件,则响应需要定义一个 Access-Control-Allow-Origin
头,否则它将受到浏览器同源策略的影响。
解决跨域的方案有很多种,详细内容可以阅读阮一峰老师的浏览器同源政策及其规避方法。
重排和重绘是关键渲染路径中的两步,本文将介绍它们两个的影响。
重绘(Repaint)是指浏览器重新绘制网页的过程。在重绘中,浏览器会根据渲染树重新绘制每个节点。
当对元素所做的更改明显改变其外观但不影响其布局时,会触发重绘。
常见的情况包括:更改元素的颜色或背景色、边框样式、不透明度、阴影、可见性等。
根据 Opera 的说法,重绘是昂贵的,因为浏览器必须验证 DOM 树中所有其他节点的可见性。
重绘是在关键渲染路径中的 Paint 阶段,将渲染树中的每个节点转换成屏幕上的实际像素,这一步通常称为绘制或栅格化。
回流/重排是指浏览器重新计算网页布局的过程。在回流中,浏览器会根据渲染树重新计算每个节点的位置和大小。
当网页中的内容、样式或者结构发生变化时,会触发回流。常见的情况如下:
回流对性能更为关键,因为它涉及到影响部分页面(或整个页面)布局的更改。
回流是在关键渲染路径中的 Layout 阶段,计算每一个元素在设备视口内的确切位置和大小。当一个元素位置发生变化时,其父元素及其后面的元素位置都可能发生变化,代价极高。
关于回流和重绘需要注意一点的是,回流必定会发生重绘,但重绘不一定会引起回流。
合成是指浏览器将多个图层合并成一个最终的屏幕显示的过程。
在浏览器中,每个元素会被渲染成一个图层。当有多个图层重叠时,浏览器会根据这些图层的层级关系,将它们合并成一个最终的显示。
合成过程是按照层级顺序进行的,先合成的图层在底层,后合成的图层在上层。在这个过程中,浏览器会根据各个图层的样式和透明度进行相应的处理,最终得到最终的显示。
但合成是一个很耗时的过程,因此,通过减少不必要的图层数量来减少合成次数可以提高性能。除了减少合成次数外,还有一种方法可以提高合成的性能:GPU 加速。
合成和 GPU 加速有着密切的关系。
在浏览器中,合成过程是由 CPU 进行的,因此当图层数量增加或者图层样式变得复杂时,合成的性能会受到影响。
而 GPU 加速则是指利用 GPU 的高性能进行合成,这样可以大大提高合成的性能。
通过 GPU 加速,浏览器可以将合成过程中的大量运算转移到 GPU 上进行,这样可以减少 CPU 的工作量,提高网页渲染性能。
可以使用以下几种方式来开启 GPU 加速:
transform: translateZ()
或 transform: rotateX()
等,可以让浏览器使用 GPU 来进行图形渲染。transform
、opacity
、filter
等可以让浏览器使用 GPU 来进行图形渲染。requestAnimationFrame
来控制动画,可以让浏览器优化动画性能并使用 GPU 来渲染。canvas
可以让浏览器使用 GPU 来进行图像处理和渲染。需要注意的是,GPU 加速并不能给所有类型的操作都带来性能提升,对于一些特殊的操作,甚至会降低性能,需要根据实际情况进行权衡。例如,使用多个层级的 3D 变换可能会导致性能问题,过多的硬件加速可能会增加带宽和内存的使用。因此,在使用 GPU 加速时,需要根据网页的具体需求和目标设备的性能来选择使用哪些方式。
在提升为合成层的情况下,会直接跳过 layout 和 paint 阶段,直接进入非主线程处理的部分,即直接交给合成线程处理。
将元素提升为合成层可以带来以下优势:
频繁的触发回流和重绘会影响网站的性能,应该尽量避免这种情况的发生。
如何减少回流和重绘,其实对应的就是减少它会触发的情况,这在上文已经有所介绍,更为详细的内容可以查阅介绍下重绘和回流(Repaint & Reflow),以及如何进行优化。
你可以在 CSS Triggers 上了解哪些 CSS 属性会引起回流、重绘和合成。
Tips:CSS Triggers 已不在维护。
需要知道一点,重绘由于 DOM 位置信息不需要更新,省去了布局过程,因此性能上优于回流。
上面介绍的比较浅,感兴趣的继续往下阅读: