浏览器如何应用CSS,它是否会影响重绘?

35
假设我们有一个带有单个样式表的HTML页面。浏览器如何将此样式表中的规则应用于HTML?我不是在问如何使其更快,我想知道渲染本身是如何处理的。
它会逐个应用每个规则,同时解析样式表并渐进性地呈现结果吗?或者,CSS文件的内容完全下载,然后进行完全评估,然后一次性应用于HTML?还是其他什么?
关于CSS规则顺序影响渲染速度的问题上发布答案后,我询问了这个问题,假定样式在样式表加载时就被呈现,因此第一个规则将在最后一个规则之前应用,而不是一次性应用所有规则。我不确定自己从哪里得到这个想法,这只是我一直以来的想法。
我在服务器上尝试了一个演示,看起来像这样:
<!DOCTYPE html>
<html>
<head>
   <title>Test</title>
   <link rel="stylesheet" href="test.css" />
</head>
<body></body>
</html>

test.css 内容:

html { background:green }
/* thousands of lines of irrelevant CSS to make the download slow */
html { background:red }

在Firefox 5中测试时,我希望一开始看到绿色,然后变成红色。但这并没有发生。我尝试了两个有冲突规则的不同样式表,得到了相同的结果。经过多次尝试,唯一让它起作用的方法是在标签中使用内联<style>块,在中使用<link>,而本身除了链接标签之外完全为空。即使在标签上使用内联

为什么我关心这个问题的一个例子是: 我从10-15个小文件创建了一个压缩的CSS文件。一切都是“命名空间”,或者使用特定的足够选择器,在许多情况下可以交换顺序。我总是将“不太相关”的CSS放在最后,认为样式会被最后应用,并且如果它们最先出现,则较重要的内容(例如页面布局或常见类)会稍后计算。我有非常强烈的感觉这是完全无关紧要的,但正在寻找事实来支持这一点。对此的回答应该同时回答链接问题。 - Wesley Murch
3个回答

16

浏览器如何将此样式表中的规则应用于HTML?

通常,这是以流式方式完成的。浏览器将HTML标签视为流,并将其可以应用到迄今为止已经看到的元素的规则应用于它们。(显然,这是一个简化版。)

一个有趣的相关问题和答案:使用CSS选择器从流解析器(例如SAX流)收集HTML元素 (这是我在寻找我想到的文章时的一个分散注意力的话题)。


啊,这里是:为什么我们没有父选择器

We often think of our pages as these full and complete documents full of elements and content. However, browsers are designed to handle documents like a stream. They begin to receive the document from the server and can render the document before it has completely downloaded. Each node is evaluated and rendered to the viewport as it is received.

Take a look at the body of an example document:

<body>
   <div id="content">
      <div class="module intro">
         <p>Lorem Ipsum</p>
      </div>
      <div class="module">
         <p>Lorem Ipsum</p>
         <p>Lorem Ipsum</p>
         <p>Lorem Ipsum <span>Test</span></p>
      </div>
   </div>
</body>

The browser starts at the top and sees a body element. At this point, it thinks it's empty. It hasn't evaluated anything else. The browser will determine what the computed styles are and apply them to the element. What is the font, the color, the line height? After it figures this out, it paints it to the screen.

Next, it sees a div element with an ID of content. Again, at this point, it thinks it's empty. It hasn't evaluated anything else. The browser figures out the styles and then the div gets painted. The browser will determine if it needs to repaint the body—did the element get wider or taller? (I suspect there are other considerations but width and height changes are the most common effects child elements have on their parents.)

This process continues on until it reaches the end of the document.

CSS gets evaluated from right to left.

To determine whether a CSS rule applies to a particular element, it starts from the right of the rule and works it's way left.

If you have a rule like body div#content p { color: #003366; } then for every element—as it gets rendered to the page—it'll first ask if it's a paragraph element. If it is, it'll work its way up the DOM and ask if it's a div with an ID of content. If it finds what it's looking for, it'll continue its way up the DOM until it reaches the body.

By working right to left, the browser can determine whether a rule applies to this particular element that it is trying to paint to the viewport much faster. To determine which rule is more or less performant, you need to figure out how many nodes need to be evaluated to determine whether a style can be applied to an element.


那么为什么样式表的内容没有逐步应用(先是绿色,然后是红色)呢? 我认为答案是外部样式表在下载时会被解析,但只有整个样式表被解析后才会被应用。在解析样式表时,浏览器会优化不必要和冗余的CSS规则。我现在没有证据来支持这一点,但这个解释听起来对我来说是合理的,并且与您所看到的情况相符,无论是外部还是内联样式。

我认为我对重新绘制如何受到提供给浏览器的新HTML影响有一个相当好的掌握,并且最近阅读了这篇文章,但我认为这不是我想要的,或者说是吗?为什么我的测试失败了?<html>标签在应用任何CSS之前就已经存在了,对吧?那么为什么样式表内容没有逐步应用(先是绿色,然后是红色)?我试图简明扼要,但我充满了问题。在我的测试中,实际上没有内容,只有制作演示所需的最少标记。在我的其他帖子中我是对还是错了? - Wesley Murch
请看我的编辑(在问题底部)。很抱歉把整篇博客都丢给你,而你已经读过了。不过,如果其他人还没有阅读过,我认为这是相关的,并且我尽量让我的答案尽可能地自成体系。 - Matt Ball
不错啊,看起来你得出了和我一样的结论,只是有些不确定。让我问你一下,如果在听到我的结果之前,你会预期测试会发生什么?明天我会回来看看投票和点赞等等,但现在我已经筋疲力尽了,必须先处理其他事情。 - Wesley Murch
转念一想,只有外部脚本是按顺序下载的。其他资源可以并行下载。我认为这只是浏览器在下载时解析整个样式表,但在解析整个样式表之前不会应用样式。(是的,我也需要睡觉了……) - Matt Ball

9
首先并且最重要的是要理解,浏览器在下载完所有CSS之前无法开始绘制页面。(请记住,W3C规范规定CSS链接仅允许在head中,因此当您像您所做的那样在body标签中链接样式表时,不同的浏览器将以不同的方式处理此情况。)
现在,网页被读取为流,并且CSS规则会随着它们被馈送到页面上的HTML元素而应用。引用下面链接的Google文章:
“当浏览器解析HTML时,它构建一个表示要显示的所有元素的内部文档树。然后,根据标准CSS级联、继承和排序规则,将元素与各种样式表中指定的样式相匹配。”
现在来回答你的问题:
它是逐个应用每个规则,同时解析样式表并渐进式地呈现结果吗?或者,CSS文件的内容完全下载后,再进行完全评估,并一次性应用于HTML?还是其他什么?
下载所有CSS,然后从上到下开始绘制文档。
在Firefox 5中进行测试,我希望一开始看到绿色,然后变成红色。但这并没有发生。我试了两个分离的样式表,有冲突规则,结果相同。 这是因为CSS先全部下载,然后遇到你的元素时只应用了红色样式,因为层叠的工作方式如此。 尝试多种组合后,唯一让它工作的方法是在中使用内联

非常有趣的视频,但我没有找到我想要的内容,尽管你强调的部分很好地解释了CSS选择器的特异性。明确一下,我不是在问如何加快速度,而是想要解释CSS样式何时/如何实际应用,或者它们如何从样式表中被传递给浏览器或渲染引擎。你的回答:“下载所有CSS,然后从上到下开始绘制文档。”听起来是正确的,但我似乎找不到任何支持或否认这一点的参考或事实,除了我的自己(可能有缺陷)的实验。 - Wesley Murch
1
@Wesley,我确实回答了你关于样式何时以及如何应用的问题。样式是从上到下应用于DOM元素的,因为HTML被视为流。更具体地说,对于每个元素,浏览器都会检查样式表中可能的CSS匹配项(它从右到左读取CSS),然后应用适用的样式(HOW)。它们直到所有CSS都被下载后才生效,这就是浏览器开始绘制页面的时间(WHEN)。在我的两个链接以及Matt的snook.ca链接中,所提出的事实确实支持这一点。 - Moses
1
我要查找参考资料的具体部分是:“在所有CSS被下载之前不会生效。” 这是由渲染引擎决定的,还是只是“工作方式”? 在我的测试中(似乎支持您的说法),<html>是唯一具有CSS规则的标记,并且在CSS被下载之前就已经存在了(除非您认为关闭标记是必需的)。 测试还在HTML标记上使用了内联style,这很有趣并且出乎意料。 另外,想知道CSS下载是否会阻塞HTML。 - Wesley Murch
@Wesley 关于哪些内容可以并行下载,哪些不能:这很复杂 - Matt Ball
@Matt:关注页面速度和yslow,我注意到了这个视频中关于<script>阻塞其他外部资源的问题,但我认为这个问题完全是关于其他事情的。例如,当我在头部使用两个单独的<link>标签时,为什么我的测试不起作用?我的问题不清楚或愚蠢吗?答案就在这里,只是还没有为我沉淀下来吗?请看我在问题本身上发布的评论中的示例,我认为它清楚地解释了我的一些担忧。 - Wesley Murch
@摩西,如果内容只有在所有样式被下载后才渲染,那我们如何遇到FOUC问题呢?http://zh.wikipedia.org/wiki/未渲染内容闪烁问题 - vancewang

1
我不确定标记的答案是否正确。根据这个来自Google开发者的链接,浏览器首先下载HTML文件,当它看到链接到外部资源的CSS文件时,它开始同时创建给定HTML文件的DOM结构和下载CSS文件,因为CSS不会影响DOM。请注意,在浏览器下载CSS文件时,不会将任何样式应用于文档。
在下载CSS文件之后(假设没有脚本文件),如果DOM构建完成,浏览器就开始将CSS属性映射到DOM树中的那些节点。之后,它创建另一个称为渲染树的树,该树构建应显示的所有对象,如矩形框。只有在完成渲染树后,才开始在屏幕上绘制。
总结一下:
- 浏览器完全下载CSS文件。 - 当浏览器下载时,不会将任何样式应用于页面。只有在下载完成后,它才开始映射规则。 - 规则仅在渲染树构建阶段应用。 - 下载CSS文件不会阻止HTML下载。您必须注意浏览器。

首先下载所有的HTML文件,然后再下载样式和脚本文件。

您可以使用Chrome的开发者控制台来检查这些内容。使用时间线选项卡查看所有内容。

时间线图像的一个示例在这里显示。我在答案开头发布的链接解释了一切。


还请查看这个网站。http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/ - Pragatheeswaran
我认为OP问的是CSS样式如何应用,而不是HTML页面呈现的顺序。 - Kira
CSS无法阻止已请求的HTML下载 - 问题是解析/渲染是否被阻止。请注意:您链接的文档说,您总结的序列是一个简化版 - 很多东西是同时进行的:“为了更好的用户体验,渲染引擎会尝试尽快在屏幕上显示内容。它不会等待所有HTML解析完成后才开始构建和布局呈现树。部分内容将被解析和显示,而进程会继续处理不断从网络中到来的其余内容。” - ToolmakerSteve

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接