CSS、JavaScript 和阻止网页解析
加载CSS资源会阻塞页面解析吗?
加载并执行JS代码会阻塞页面解析吗?
正常脚本加载和执行
实验
最近,我偶然看到一篇关于 CSS 文件加载问题的文章,这个问题会降低页面素材的处理速度。我读了这篇文章,想学习一些新知识,但我发现它说的不太对。于是,我针对这个问题做了一些研究,并尝试了 CSS 和 JavaScript 的加载方法。
加载CSS资源会阻塞页面解析吗?
首先我要说的是,本节标题中的问题毫无疑问可以得到肯定的回答。加载 CSS 文件不仅会阻止 HTML 代码解析,还会阻止 JavaScript 代码执行。
首先,我建议你先尝试一下。为此,我们需要相应地配置浏览器。我们将从 CDN 下载 CSS 文件,因此需要限制 Google Chrome 浏览器的网速。为此,请在开发者工具的“性能”选项卡中,将“网络”参数值更改为“慢速 3G”。我们将探索下一页:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta data-fr-http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">
<script>
document.addEventListener('DOMContentLoaded', () => {
console.log('DOMContentLoaded');
})
</script>
<script>
console.log('script');
Promise.resolve(1).then(res => {
console.log('then');
});
</script>
</head>
<body>
<h1>hello</h1>
</body>
</html>
我们从 CDN 下载了 CSS 文件,但由于人为限制了网络连接速度,加载样式需要一些时间。因此,在 CSS 文件加载完成之前,JavaScript 控制台没有任何内容,页面内容也无法显示在屏幕上。我们看到的情况表明,CSS 加载阻碍了其他页面内容的加载和处理。
加载并执行JS代码会阻塞页面解析吗?
当然,加载和处理 JS 文件会阻塞页面解析。不过,为了解决这个问题,你可以在将脚本连接到页面时使用属性和标签defer async <script>
。现在我们来研究一下它们对页面加载的影响。
正常脚本加载和执行
如果标签<script>
未使用async
或属性defer
——页面内容加载和处理过程如下图所示。加载 JS 文件并执行其中包含的代码会阻止 HTML 解析。
使用
<script>
不带 async 和 defer 属性的标签
在这里以及以后,我们将使用以下颜色符号。
HTML 解析 — HTML 解析;HTML 解析暂停 — HTML 解析暂停;脚本下载 — 脚本加载;脚本执行 — 脚本执行
使用<script>
带有 async 属性的标签
当浏览器处理<script>
带有属性的标签时async
,JavaScript 代码会异步加载。加载完成后,脚本代码会立即执行。然而,JS 代码的执行会阻塞 HTML 解析。
使用<script>
带有 defer 属性的标签
如果标签<script>
包含属性defer
— 则脚本代码会异步加载。但是,代码加载完成后,只有在 HTML 代码解析完成后才会执行。
实验
让我们尝试一下async
和属性defer
。让我们从下一页开始:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta data-fr-http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DomContentLoaded</title>
</head>
<body>
<script src="http://code.jquery.com/jquery-1.4.4.min.js">
</script>
<script src="./index.js"/> // 0
<script src="./index2.js"/> // 2
<script >
console.log('inline');
Promise.resolve().then(res=>{
console.log('then');
})
</script>
<div id="hello">hello world</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
console.log('DOMContentLoaded');
})
</script>
</body>
</html>
此页面除了jquery-1.4.4.min.js
从 CDN 下载脚本外,还加载了几个自己的脚本 -index.js
和index2.js
。以下是它们的代码。
文件index.js
:
Promise.resolve().then((res) => {
console.log('index1');
return res;
});
文件index2.js
:
Promise.resolve().then((res) => {
console.log('index2');
return res;
});
当此页面加载时,JS 控制台将获得如下所示的内容。
因此,我们有证据表明,加载和处理 JS 文件会阻塞 HTML 代码的渲染。脚本输出的消息会先于 DOM 内容加载完成的消息出现在控制台中。
现在让我们看一下<script>
在标签中使用属性的脚本如何表现<async>
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta data-fr-http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DomContentLoaded</title>
</head>
<body>
<script async src="http://code.jquery.com/jquery-1.4.4.min.js">
</script>
<script src="./index.js"></script>
<script src="./index2.js"/></script>
<script>
console.log('inline');
Promise.resolve().then(res=>{
console.log('then');
})
</script>
<div id="hello">hello world</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
console.log('DOMContentLoaded');
})
</script>
</body>
</html>
我们来看看控制台里显示的内容。
jQuery 库脚本是异步加载的。控制台中的内容会在加载之前显示。如果库脚本加载速度太慢,它不会干扰 HTML 代码的解析。该消息DOMContentLoaded
可以在异步脚本加载和执行之前或之后显示。应用该属性时defer
,脚本将异步加载,等待文档素材处理完毕,然后在 DOMContentLoaded 事件之前执行。
您是否遇到过阻止网页内容处理的问题?
文章来源:https://dev.to/ra1nbow1/css-javascript-and-blocking-web-page-parsing-610