script脚本对DOM的影响
当HTML解析器解析HTML,如果遇到script
标签,普通的script
标签会暂停对DOM解析渲染,因为该脚本可能会修改DOM。 这里有三种情况:普通脚步、defer、async。 ==defer、async只对外联script脚本文件有效, 内联script脚本设置无效。==
问:
script
标签总是会触发Paint吗?
回答:script
标签时,会触发一次Paint,浏览器会将script
标签之前的元素渲染出来。但也并不是所有的script
标签都会触发Paint。
head
中的script
标签是不会触发的,毕竟此时body
还没有解析,触发Paint也看不到任何内容。- inline(内联:将代码直接嵌入到HTML文档的元素中,而不是通过外部文件引用的方式) 的
script
也不会触发Paint。因此,建议
script
标签放在body
结束标签之前,这样不会不会阻塞页面整体内容的DOM解析和渲染。
1、普通脚本
- 文档解析过程中,如果遇到普通脚本就会直接下载脚本,下载会阻止DOM的解析渲染
- 如果是多个脚本,则并行下载,不论哪个先下载完,都要按HTML中的顺序执行,即使后面的比前面的先下载完,也要等前面的执行完才能执行
- 执行脚本会阻止页面的解析渲染
- 执行完脚本继续页面的解析渲染
- 执行完script脚本和页面解析渲染完, 才会依此触发
DOMContentLoaded
、loaded
事件
2、defer,
- 文档执行时,当遇到有
defer
属性的script
标签时,则脚本的下载则在后台运行,下载不会阻止DOM解析渲染 - 多个
defer
属性的script
标签,则在后台并行下载 - 脚本的执行需要等到页面解析完成才能进行
- 当页面解析渲染完毕后, 会等到所有的
defer
脚本下载完毕并按照顺序执行,执行完毕后会触发DOMContentLoaded
事件。 - 如果
defer
脚本下载较慢,在下载完前, 页面解析渲染已完毕; 等所有的defer
脚本下载完后, 才按照顺序执行defer
脚本。执行完毕后会触发DOMContentLoaded
事件。
3、async
- 文档解析时,当遇到有
async
属性的script
标签时,则脚本的下载则在后台运行,下载不会阻止DOM解析渲染 - 多个
async
属性的script
标签,则在后台同时并行下载 async
脚本的执行会阻止页面的解析渲染- 遵循先下载完先执行,执行不按照HTML页面的中脚本顺序
- async脚本的下载和执行不计入DOMContentLoaded事件统计。
link标签对DOM的影响
1、link标签不会阻塞DOM解析但会阻塞DOM渲染
link
标签并不阻塞DOM的解析,但会阻塞DOM的渲染。浏览器并行解析生成DOM Tree 和 CSSOM Tree,当两者都解析完毕,才会生成render tree,页面才会渲染。所以应尽量减小引入样式文件的大小,提高首屏展示速度。 注意:案例中CSS资源为外网资源,所以并不会直接就加载出来,可以在页面看到渲染的过程,当然可以直接开vpn,css资源几乎秒加载,页面也秒渲染
javascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
setTimeout(() => {
console.log(document.getElementById('num'));
}, 0);
document.addEventListener('DOMContentLoaded', () => {
console.log('dom parse done');
})
</script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.css">
</head>
<body>
<div id="num">
i am content a.
</div>
<div>
i am content b.
</div>
</body>
</body>
</html>
初始加载页面的时候,控制台打印出来两条数据,但是页面并没渲染,此时CSS资源正在加载中 之后CSS资源一直加载,直到加载失败,页面才渲染完成,说明,link标签加载CSS资源时阻止了页面渲染
2、link标签会阻塞JS执行
JS运行时,有可能会请求样式信息,如果此时还没有加载和解析样式,js就有可能会得到错误的回复,产生很多问题。因此浏览器在link
标签的加载和解析过程中,会禁止脚本运行。
案例一
javascript
<!DOCTYPE html>
<html lang="en">
<head>
<tilte>title</title>
<script>
console.log(Date.now());
</script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.css">
<script>
console.log(Date.now());
</script>
</head>
<body>
<div id="num">
i am content a.
</div>
<div>
i am content b.
</div>
</body>
</body>
</html>
初始页面加载,此时CSS资源正在加载中,所以body中的内容还没渲染出来,并且link
标签下的script
中的console
也还未执行,所以说,link标签加载CSS资源时也阻塞的JS的执行 之后,因为CSS资源加载失败,所以开始执行下面的script
,并且打印出console
内容-当前时间。
案例二
javascript
<html>
<head>
<tilte>title</title>
<!--大文件,加载时间长-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.css">
</head>
<body>
<div>
i am content a.
</div>
<!--js小文件,加载时间短-->
<script src='test.js'></script>
<div>
i am content b.
</div>
</body>
</html>
页面初始加载时,CSS资源一直在加载,body
中的script一直没有加载出来,可以看到控制台并没有打印任何东西。所以说link
标签会阻止JS执行
当CSS资源加载完成或者加载失败后就执行了script
脚本,可以看到控制台打印出来js执行完毕
,且此时页面已经渲染出来
3、link和@import的区别
- 用法:
javascript
<link href="a.css" rel="stylesheet" type="text/css">
<style>
@import url('b.css');
</style>
- 功能上:
link
功能较多,可以定义 RSS、Rel 等,而@import
只能用于加载 css; - 加载顺序:
link
标签让浏览器知道这是个样式表文件,html的解析和渲染不会暂停,css文件的加载是同时进行的,这不同于在style标签里面的内置样式;@import
添加的样式是在页面载入之后再加载,这可能会导致页面因重新渲染而闪烁。@import
会影响浏览器的并行下载,使得页面在加载时增加额外的延迟,增添了额外的往返耗时,而且多个@import
可能会导致下载顺序紊乱。比如: 一个css文件index.css
包含了以下内容:@import url("reset.css")
,那么浏览器就必须先把index.css
下载、解析和执行后,才下载、解析和执行第二个文件reset.css
- DOM操作:
link
支持DOM操作改变样式,由于 DOM 方法是基于文档的,无法使用@import
的方式插入样式 - 兼容性:
@import
是 CSS2.1提出的语法,老版本的浏览器可能不支持;link
标签作为 HTML 元素,不存在兼容性问题。