前言
大家好,我是辉夜真是太可爱啦 。
在上一文中,我们大致描述了浏览器的渲染过程。但是,由于 HTML 文档的解析过程,也是挺复杂的。所以我们专门将它抽离了出来。
先来说加载的意义:下载 + 执行。
CSS
1. CSS 文件是并行下载的
2. CSS 并不会阻塞构建 DOM树
3. CSS 的下载不会阻塞后面 JS 的下载,但是 JS 下载完成后,被阻塞执行
但是, CSS 加载会影响 JS 代码的执行,这个起初我也感觉很不可思议,让我们来看个例子:
为了方便测试,大家可以把谷歌浏览器的网速进行设置
<!DOCTYPE html>
<html lang="en">
<head>
<title>css阻塞</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
alert('文档开始解析了!');
</script>
<link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.css" rel="stylesheet">
</head>
<body>
<h1>这是红色的</h1>
</body>
<script>
alert('文档解析结束了!');
</script>
</html>
文档会从上往下进行解析,先执行 alert('文档开始解析了!'); ,然后,遇到 link 标签,加载外链 CSS ,直到加载完成之后,才会执行下面的 alert('文档解析结束了!'); 。
这是由于 JS 可能会获取或者改变元素的样式,所以浏览器会按照顺序,等上面的 CSS 加载解析完成之后,再执行下面的 JS 。
JS
-
现代浏览器会并行加载 JS 文件,但是按照书写顺序执行代码
-
加载或者执行 JS 时会阻塞构建
DOM树,只有等到js执行完毕,浏览器才会继续解析 DOM 。没有DOM树,浏览器就无法渲染,所以当加载很大的 JS 文件时,可以看到页面很长时间是一片空白。
这是由于 JS引擎线程 与 GUI渲染线程 互斥。
至于互斥的初衷,主要是因为加载的 JS 中可能会创建,删除节点等,这些操作会对 DOM树 产生影响,如果不阻塞,等浏览器解析完标签生成 DOM树后, JS 修改了某些节点,那么浏览器又得重新解析,然后重新生成 DOM树,性能比较差。
如果你不想 JS 阻塞你的 DOM树 生成,也是有方法的。
defer / async
defer 和 async 都是作用于 外链JS 的。对于 内部JS 是没有效果的。
defer 和 async 都是异步的,主要的区别在于执行顺序以及执行的时间。
async 标志的脚本文件一旦加载完成就会立即执行,并且不会按照书写顺序,谁下载好了就直接执行。所以适用于那些没有代码依赖顺序,并且没有 DOM操作 的脚本文件。
defer 标志的脚本文件会严格按照书写顺序执行,并且,会在 DOMContentLoaded 事件之前(也就是页面DOM加载完成时)执行。适用于有 DOM操作 ,或者是有代码依赖顺序的脚本文件。