关键渲染路径

270 阅读4分钟

critical-rendering-path

Every time the browser processes HTML markup, it goes through all of the steps above: convert bytes to characters, identify tokens, convert tokens to nodes, and build the DOM tree. This entire process can take some time, especially if we have a large amount of HTML to process.

image.png

上面这张图可以看到 html 解析成 dom 花费的时间,有时候这里也是一个优化的点。

Why does the CSSOM have a tree structure? When computing the final set of styles for any object on the page, the browser starts with the most general rule applicable to that node (for example, if it is a child of a body element, then all body styles apply) and then recursively refines the computed styles by applying more specific rules; that is, the rules "cascade down."

这里说到了一个 css 的特性,就是 CSS 在中文中本身指的是层叠样式表* (Cascading Style Sheets),从我入行开始就记得有人考过这个题目,这里的层叠是什么意思?其实上面这句话就说了这个意思,是因为 css 会被 chrome,准确地来说是浏览器,转换成 cssom,然后 cssom 是一种 tree 的数据形式。所以子规则会继承父规则的样式,因此是层叠的。

文章举了个例子

https://googlesamples.github.io/web-fundamentals/fundamentals/performance/critical-rendering-path/basic_dom.html

打开这个例子,然后通过 performance reload 查看,会发现在 parse html 的时候就发起了 style.css 的请求。

image.png

By default, CSS is treated as a render blocking resource, which means that the browser won't render any processed content until the CSSOM is constructed. Make sure to keep your CSS lean, deliver it as quickly as possible, and use media types and queries to unblock rendering.

也就是说在首屏渲染的时候,同时需要 html 和 css,构建好 dom 和 cssom 之后才开始首屏渲染,所以 css 也会阻塞首屏渲染。

First, notice that in the above example our inline script is near the bottom of the page. Why? Well, you should try it yourself, but if we move the script above the span element, you'll notice that the script fails and complains that it cannot find a reference to any span elements in the document; that is, getElementsByTagName(‘span') returns null. This demonstrates an important property: our script is executed at the exact point where it is inserted in the document. When the HTML parser encounters a script tag, it pauses its process of constructing the DOM and yields control to the JavaScript engine; after the JavaScript engine finishes running, the browser then picks up where it left off and resumes DOM construction.

In other words, our script block can't find any elements later in the page because they haven't been processed yet! Or, put slightly differently: executing our inline script blocks DOM construction, which also delays the initial render.

dom 的构建过程是按照顺序的,而 inline script 会阻塞这种构建。也就是当遇到 script 标签的时候,浏览器会把执行权限交给 js 引擎,等 js 引擎执行完 inline script 之后再构建后面的 dom 。

Another subtle property of introducing scripts into our page is that they can read and modify not just the DOM, but also the CSSOM properties. In fact, that's exactly what we're doing in our example when we change the display property of the span element from none to inline. The end result? We now have a race condition.

What if the browser hasn't finished downloading and building the CSSOM when we want to run our script? The answer is simple and not very good for performance: the browser delays script execution and DOM construction until it has finished downloading and constructing the CSSOM.

而 inline script 还会被 cssom 的构建过程阻塞,也就是要等 cssom 也构建完成之后 js 引擎才会继续执行。

Whether we use a

不管是 inline script 还是 script 标签形式的 js 都会阻塞首屏渲染。

By default all JavaScript is parser blocking. Because the browser does not know what the script is planning to do on the page, it assumes the worst case scenario and blocks the parser. A signal to the browser that the script does not need to be executed at the exact point where it's referenced allows the browser to continue to construct the DOM and let the script execute when it is ready; for example, after the file is fetched from cache or a remote server.

To achieve this, we mark our script as async:

阻塞的效果通过给 script 标签添加 async 的方式可以取消。

Notice that our "awesome photo" did not block the domContentLoaded event. Turns out, we can construct the render tree and even paint the page without waiting for each asset on the page: not all resources are critical to deliver the fast first paint. In fact, when we talk about the critical rendering path we are typically talking about the HTML markup, CSS, and JavaScript. Images do not block the initial render of the page—although we should also try to get the images painted as soon as possible.

图片的请求不会阻塞 dom 的解析和首屏渲染。

关于 script 标签中 async 和 defer 的作用,可以看看 这篇文章。 原文看这里

这篇文章做了实验 JS 与 CSS 阻塞 DOM 渲染解析的情况详解

还有关于字体阻塞首屏渲染的情况,可以看optimize-webfont-loading

一个不错的测试网站性能的工具

www.webpagetest.org/

关于帧(frame)的一篇不错文章

aerotwist.com/blog/the-an…

美团的文章

tech.meituan.com/2018/11/15/…

mdn

developer.mozilla.org/en-US/docs/…

这里面有一句话

While the DOM construction is incremental, CSSOM is not.

这里面提到了 DOM 是 incremental 的,可见 dom 是一遍解析一遍构建的。