您的位置:首页 > Web前端 > CSS

浏览器渲染 defer async css cssom

2017-11-13 11:21 267 查看


转自:http://web.jobbole.com/92765/


阻塞渲染:CSS 与 JavaScript

谈论资源的阻塞时,我们要清楚,现代浏览器总是并行加载资源。例如,当 HTML 解析器(HTML Parser)被脚本阻塞时,解析器虽然会停止构建 DOM,但仍会识别该脚本后面的资源,并进行预加载。

同时,由于下面两点:

默认情况下,CSS 被视为阻塞渲染的资源,这意味着浏览器将不会渲染任何已处理的内容,直至 CSSOM 构建完毕。
JavaScript 不仅可以读取和修改 DOM 属性,还可以读取和修改 CSSOM 属性。

存在阻塞的 CSS 资源时,浏览器会延迟 JavaScript 的执行和 DOM 构建。另外:

当浏览器遇到一个 script 标记时,DOM 构建将暂停,直至脚本完成执行。
JavaScript 可以查询和修改 DOM 与 CSSOM。
CSSOM 构建时,JavaScript 执行将暂停,直至 CSSOM 就绪。

所以,script 标签的位置很重要。实际使用时,可以遵循下面两个原则:

CSS 优先:引入顺序上,CSS 资源先于 JavaScript 资源。
JavaScript 应尽量少影响 DOM 的构建。

浏览器的发展日益加快(目前的 Chrome 官方稳定版是 61),具体的渲染策略会不断进化,但了解这些原理后,就能想通它进化的逻辑。下面来看看 CSS 与 JavaScript 具体会怎样阻塞资源。


CSS

JavaScript

12<style> p { color: red; }</style><link rel="stylesheet" href="index.css">
这样的 link 标签(无论是否 inline)会被视为阻塞渲染的资源,浏览器会优先处理这些 CSS 资源,直至 CSSOM 构建完毕。渲染树(Render-Tree)的关键渲染路径中,要求同时具有 DOM 和 CSSOM,之后才会构建渲染树。即,HTML 和 CSS 都是阻塞渲染的资源。HTML 显然是必需的,因为包括我们希望显示的文本在内的内容,都在 DOM 中存放,那么可以从 CSS 上想办法。最容易想到的当然是精简 CSS 并尽快提供它。除此之外,还可以用媒体类型(media type)和媒体查询(media query)来解除对渲染的阻塞。JavaScript

1

2

3

<link
href="index.css"
rel="stylesheet">

<link
href="print.css"
rel="stylesheet"
media="print">

<link
href="other.css"
rel="stylesheet"
media="(min-width:
30em) and (orientation: landscape)">

第一个资源会加载并阻塞。

第二个资源设置了媒体类型,会加载但不会阻塞,print 声明只在打印网页时使用。

第三个资源提供了媒体查询,会在符合条件时阻塞渲染。


JavaScript

JavaScript 的情况比 CSS 要更复杂一些。观察下面的代码:

JavaScript

1234567891011<p>Do not go gentle into that good night,</p><script>console.log("inline")</script><p>Old age should burn and rave at close of day;</p><script src="app.js"></script><p>Rage, rage against the dying of the light.</p> <p>Do not go gentle into that good night,</p><script src="app.js"></script><p>Old age should burn and rave at close of day;</p><script>console.log("inline")</script><p>Rage, rage against the dying of the light.</p>
这样的 script 标签会阻塞 HTML 解析,无论是不是 inline-script。上面的 P 标签会从上到下解析,这个过程会被两段 JavaScript 分别打算一次(加载、执行)。所以实际工程中,我们常常将资源放到文档底部。

改变阻塞模式:defer 与 async

为什么要将 script 加载的 defer 与 async 方式放到后面呢?因为这两种方式是的出现,全是由于前面讲的那些阻塞条件的存在。换句话说,defer 与 async 方式可以改变之前的那些阻塞情形。首先,注意 async 与 defer 属性对于 inline-script 都是无效的,所以下面这个示例中三个 script 标签的代码会从上到下依次执行。JavaScript

1

2

3

4

5

6

7

8

9

10

<!--
按照从上到下的顺序输出
1
2
3
-->

<script
async>

console.log("1");

</script>

<script
defer>

console.log("2");

</script>

<script>

console.log("3");

</script>

故,下面两节讨论的内容都是针对设置了 src 属性的 script 标签。


defer

JavaScript

123<script src="app1.js" defer></script><script src="app2.js" defer></script><script src="app3.js" defer></script>
defer 属性表示延迟执行引入的 JavaScript,即这段 JavaScript 加载时 HTML 并未停止解析,这两个过程是并行的。整个 document 解析完毕且 defer-script 也加载完成之后(这两件事情的顺序无关),会执行所有由 defer-script 加载的 JavaScript 代码,然后触发 DOMContentLoaded 事件。defer 不会改变 script 中代码的执行顺序,示例代码会按照 1、2、3 的顺序执行。所以,defer 与相比普通 script,有两点区别:载入 JavaScript 文件时不阻塞 HTML 的解析,执行阶段被放到 HTML 标签解析完成之后。

async

JavaScript

1

2

3

<script
src="app.js"
async></script>

<script
src="ad.js"
async></script>

<script
src="statistics.js"
async></script>

async 属性表示异步执行引入的 JavaScript,与 defer 的区别在于,如果已经加载好,就会开始执行——无论此刻是 HTML 解析阶段还是 DOMContentLoaded 触发之后。需要注意的是,这种方式加载的 JavaScript 依然会阻塞 load 事件。换句话说,async-script 可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行。

从上一段也能推出,多个 async-script 的执行顺序是不确定的。值得注意的是,向 document 动态添加 script 标签时,async 属性默认是 true,下一节会继续这个话题。


document.createElement

使用 document.createElement 创建的 script 默认是异步的,示例如下。

JavaScript

1console.log(document.createElement("script").async); // true
所以,通过动态添加 script 标签引入 JavaScript 文件默认是不会阻塞页面的。如果想同步执行,需要将 async 属性人为设置为 false。如果使用 document.createElement 创建 link 标签会怎样呢?JavaScript

1

2

3

4

const
style
=
document.createElement("link");

style.rel
=
"stylesheet";

style.href
=
"index.css";

document.head.appendChild(style);
//
阻塞?

其实这只能通过试验确定,已知的是,Chrome 中已经不会阻塞渲染,Firefox、IE
在以前是阻塞的,现在会怎样我没有试验。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐