要更详细地解释,我们需要深入了解渲染引擎的工作方式。
渲染引擎解析HTML文档并创建两个树:内容树和渲染树。内容树包含所有DOM节点。渲染树包含所有样式信息(CSSOM)和仅用于呈现页面的DOM节点。
一旦渲染树被创建,浏览器会经过两个过程:应用布局和绘制每个DOM节点。应用布局意味着计算DOM节点在屏幕上出现的确切坐标。绘制意味着实际渲染像素并应用样式属性。
这是一个逐步的过程:浏览器不会等待所有HTML都被解析。部分内容将被解析和显示,而进程将继续处理其余从网络传来的内容。
您可以在浏览器中看到此过程正在发生。例如,打开Chrome开发者工具并加载您选择的站点。
在记录Network
标签中的活动后,您会注意到解析开始于下载文档时。它识别资源并开始下载它们。蓝色垂直线表示 DOMContentLoaded
事件,红色垂直线表示 load
事件。
记录时间轴可以更深入地了解底层发生的情况。我已经包含了上面的截图作为示例,以表明在解析文档时绘制正在发生。请注意,初始绘制发生在它继续解析文档的另一部分之前。这个过程会持续进行,直到到达文档的结尾。<script>
放在文档末尾并不会提高总解析时间。它确实可以增强用户体验,因为解析和绘制过程不会被需要执行的 <script>
中断。defer
和/或 async
标记资源,可以解决此问题。 async
在 HTML 解析期间下载文件,并在下载完成后暂停 HTML 解析器以执行它。 defer
在 HTML 解析期间下载文件,并仅在解析器完成后执行它。<script>
的阻塞问题。引擎在下载和执行脚本时进行预解析(并运行 HTML 树构建!)。Firefox 和 Chrome 使用这种技术。// Results in an unbalanced tree
<script>document.write("<div>");</script>
// Results in an unfinished token
<script>document.write("<div></div");</script>
以下资源值得您花时间阅读:
<script>
放在 <body>
结束标签处是最佳实践,这样页面可以在脚本下载前渲染出一些内容。<head>
中,用户可能需要很长时间才能看到网页上的任何内容。想象一下你有这样一个 html 页面:<html>
<head>
<!-- this huge.js takes 10 seconds to download -->
<script src="huge.js"></script>
</head>
<body>
<div>
My most fancy div!
</div>
</body>
</html>
// huge.js
(function () {
// Some CPU intensive JS operations which take 10 second to complete
})();
<script>
标签后立即开始执行那些占用 CPU 的 JS。它将 阻塞 解析其余的 HTML 内容。因此,在这种情况下,用户在下载和执行 JavaScript(总共需要 20 秒)之前无法看到他的花哨的 div。DOMContentLoaded
来检测初始 DOM 是否已加载和解析。并且您在上一段中的陈述是相当正确的:每次 HTML 解析器看到一个 <script>
,它都会同步下载和执行它(见注意 2)。在所有 <script>
都被执行并且所有 HTML 被解析之后,DOMContentLoaded
将被触发。
注意 1:DOMContentLoaded
不会等待 CSS 和图像。
注意 2:大多数浏览器都有“推测解析”功能。如果有多个 JavaScript 文件,则它们将同时下载。但是,它们仍将由主线程按顺序执行。根据我的理解,答案是是的,浏览器将尝试尽快绘制。也就是说,绘画引擎不会等待渲染树完全准备好。因此应该有一个单独的线程来处理绘画。或者说,页面在解析 DOM 树的同时绘制页面?
while DOM != parsed:
if current_node.has_child():
current_node = child_node
execute_node()
elif current_node.has_sibling():
current_node = sibling_node
execute_node()
elif current_node.has_parent_sibling():
current_node = parent_sibling
execute_node()
else:
current_node = parent_node
<script>
元素,它仍然可以相对快速地呈现给用户,以便在您所在的页面下载完成后,它会在整个网页加载完成之前呈现给用户。令人惊讶的是,我测试过的所有浏览器都在整个文件下载完成之前显示了该网页:Chrome、Firefox、Safari、Edge、Opera Mini,甚至是Internet Explorer 11。 - Jack G