浏览器渲染过程

235 阅读3分钟

主要流程

  • 解析HTML并构建DOM树
  • 解析CSS并构建CSS Object Modal树
  • 将DOM树和CSSOM树组合成一个Render树
  • Layout 在Render树上计算每个节点的大小和位置,叫做布局,随后对布局进行重新计算对过程叫做回流(reflow)。
  • Paint 将元素的可视部分绘制到屏幕上,包括文本、颜色、边框、阴影和替换的元素。

script阻塞问题

    在解析HTML时,当遇到一个script标签时,会阻塞解析,待script脚本加载并执行完成之后再继续解析(未设置async defer属性时)。当代浏览器使用预加载扫描器preload scanner)优化了这一过程,它会提前找到脚本和图片等外部资源并下载,而不必等到解析器执行到该部分,避免了HTML阻塞情况下后续资源到下载。为了避免阻塞解析,可以给script标签设置defer和async属性。两者的差别在于,设置defer的脚本在HTML解析完成后会立即执行,并且这个过程在事件DOMContentLoaded之前。而async的执行时机不受解析情况的影响,设置async属性当脚本下载完成后就会立即执行。这时可能HTML解析尚未完成,那么它会阻塞HTML解析。也可能HTML解析已经完成并且DOMContentLoaded时间已经触发。

CSS阻塞问题

    通常情况下,CSS解析过程和HTML解析是可以并行的,并不会造成阻塞。但是CSS解析会阻塞script脚本的执行,避免script脚本操作了尚未解析到到css内容而报错,同时script又会阻塞HTML。这一点不同浏览器的处理上有些差异,Firefox会果断终止scripts脚本的执行,而WebKit内核的浏览器只会在脚本试图访问尚未加载解析完成的样式属性时终止它。以上为理论情况,但是按照Mozilla官方的解释,构建CSSOM树其实是非常非常快的,这样看的话它的阻塞情况应该影响很小。

HTML解析是否阻塞渲染

    大部分文章里说,在DOM树和CSSOM树解析完成之前,浏览器不会进行后续渲染步骤,实际情况是这样吗?

// index.html

<html>
    <head>
        <link rel="stylesheet" href="./index.css">
    </head>
    <body>
        <div class="header">Header</div>
        <script src="./index.js"></script>
        <div class="footer">Footer</div>
    </body>
</html>
// index.js

document.addEventListener('DOMContentLoaded', function() {
  console.log('dom content loaded');
})

window.onload = function() {
  console.log('on load');
}

console.log('111');
debugger;
console.log('222');
// index.css

.header {
  width: 100px;
  background-color: #888888;
}

.footer {
  width: 200px;
  background-color: #ffff00;
}

现在考虑以上脚本,script脚本被header和footer夹在中间。在script脚本中使用debugger来终止执行,那么在触发debugger时屏幕上有内容吗?事实上,浏览器已经将header渲染了出来,如下图 所以,事实上浏览器的渲染过程是一个渐进的过程,即使正在解析某个节点的子节点时,浏览器也可以提前将该节点渲染出来,之后再进行reflow和repaint更新渲染内容。

It's important to understand that this is a gradual process. For better user experience, the rendering engine will try to display contents on the screen as soon as possible. It will not wait until all HTML is parsed before starting to build and layout the render tree. Parts of the content will be parsed and displayed, while the process continues with the rest of the contents that keeps coming from the network.

我们再给script加上defer属性,此时页面上已经渲染出了header和footer的内容。

参考文章

developer.mozilla.org/zh-CN/docs/…

zhuanlan.zhihu.com/p/74792085

www.html5rocks.com/en/tutorial…

juejin.cn/post/684490…

stackoverflow.com/questions/3…