【前端面试专栏】script脚本以及link标签对DOM的影响

2023-11-28 14:04:44 浏览数 (1)

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脚本和页面解析渲染完, 才会依此触发DOMContentLoadedloaded事件

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资源为外网资源,所以并不会直接就加载出来,可以在页面看到渲染的过程,当然可以直接开v**,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资源正在加载中一直加载,直到加载失败,页面才渲染完成,说明,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 元素,不存在兼容性问题。

我正在参与2023腾讯技术创作特训营第三期有奖征文,组队打卡瓜分大奖!

0 人点赞