这一章内容比较少,讲的是如何在HTML中引使用js代码。个人列了几个重点:js代码使用的方式、外部引入js文件两个重要的属性——defer和async。
实际的开发中,js文件基本都是外部引入,极少会在HTML中写js代码,而且基本都是单个js入口,外加一些常用库。
使用方式
在HTML中使用script元素来加入Javascript代码,方式有两种:
- 嵌入代码
- 通过元素src属性引入外部脚本
一般下,推荐使用外部文件来包含Javascript代码,这样的好处有:
- 可维护性:js文件集中放在同一目录,并拥有适合的文件结构,易于管理;开发人员可以在不触及HTML标记的情况下,集中精力编辑js代码。
- 可缓存: 浏览器会缓存已加载的外部js文件,不同的页面使用同一个js文件,则无需多次加载,提高速度。
- 适应未来
defer延迟脚本
- 脚本会被延迟到整个页面都解析完毕后再运行,效果类似于把JS文件引入放在 body 元素的最后,如此保证DOM操作不会出现问题。
- 理论上,多个延迟脚本会按照出现的先后顺序执行。但现实中,延迟脚本并一定会按照顺序执行,不同浏览器实现方式可能会不一样。
- 一个页面最好只包含一个延迟脚本。
用例子来说明:
例1:延迟脚本功能类似于把引入放在body之后
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>第二章demo</title>
<script src="./outerJs.js" defer></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
outerJs.js
function func() {
let ele = document.getElementById('app');//操作DOM
ele.innerText = `Hello, Welcome to Chapter 2!`;
};
func();
执行结果:页面 #app 元素正常显示
Hello, Welcome to Chapter 2!
例2:同时存在2个延迟脚本
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>第二章demo</title>
<script src="./outerJs-defer01.js" defer></script>
<script src="./outerJs-defer02.js" defer></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
outerJs-defer01.js
console.log('This log comes from defer-loaded script file 01!')
outerJs-defer02.js
console.log('This log comes from defer-loaded script file 02!')
执行结果:执行顺序是1到2(这里1、2指两个文件),正常顺序执行。
async异步脚本
- 立即下载脚本,不影响页面中的其他操作,比如下载其他资源或等待加载其他脚本。
- 同一页面下多个异步脚本时,执行顺序也是不确定的,要确保两者之间互不依赖,否则会出现意外的bug。(那个文件加载完,那个先执行)
- 建议异步脚步不要在加载期间修改DOM。
- 异步脚步一定会在页面load事件前执行。
例3:异步脚本
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>第二章demo</title>
<script src="./outerJs.js" async></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
outerJs.js
function func() {
let ele = document.getElementById('app');//操作DOM
ele.innerText = `Hello, Welcome to Chapter 2!`;
};
func();
执行结果:页面 #app 元素正常显示
Hello, Welcome to Chapter 2!
。理解为加载异步脚本时,同时完成了DOM加载,所以可以正常操作DOM。
例4:同时存在2个异步脚本,一个本地文件,一个是lodash的cdn引入
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>第二章demo</title>
<script src="https://cdn.bootcss.com/lodash.js/4.17.10/lodash.min.js" async></script>
<script src="./outerJs-async01.js" async></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
outerJs-async01.js
console.log('This log comes from async-loaded script file 01!' + 'Timestamp = ' + _.now());
执行结果:报错,执行
outerJs-async01.js
脚本时lodash
没有加载完成。理解为,本地资源outerJs-async01.js
先加载完成,先执行;网络资源lodash.min.js
还未加载完成。所以,异步脚步的执行顺序是不确定的,“那个先加载完,那个先执行”。
例5:关于页面加载时触发的事件、延迟脚本、异步脚本的执行顺序
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>第二章demo</title>
<script type="text/javascript">
console.log('resolve body JavaScript');
window.addEventListener('load',function(){//监听window load事件
console.log('window load');
});
document.addEventListener('readystatechange',function(){//监听document readystatechange事件
console.log('document ' + document.readyState);
});
document.addEventListener('DOMContentLoaded',function(){//document DOMContentLoaded事件
console.log('document DOMContentLoaded');
});
</script>
<!-- 添加一个延迟脚本和一个异步脚本 -->
<script src="./outerJs-defer01.js" defer></script>
<script src="./outerJS-async01.js" async></script>
</head>
<body>
<div id="app"></div>
<!-- 页面中添加一些需要加载的图片元素 -->
<h1>测试页面加载时,事件触发次序</h1>
<img src="http://c.hiphotos.baidu.com/image/pic/item/09fa513d269759eea79bc50abbfb43166c22df2c.jpg" alt="">
<h1>测试页面加载时,事件触发次序</h1>
<img src="http://h.hiphotos.baidu.com/image/pic/item/279759ee3d6d55fb75ed26e764224f4a21a4ddcc.jpg" alt="">
<h1>测试页面加载时,事件触发次序</h1>
</body>
</html>
outerJs-defer01.js
console.log('This log comes from defer-loaded script file 01!');
outerJs-async01.js
console.log('This log comes from async-loaded script file 01!');
执行结果:
延迟脚本在readystatechange 事件的 interactive
状态之后DOMContentLoaded
事件之前执行;
异步脚本DOMContentLoaded
事件之后complete
状态 以及window load事件
之前执行。
关于页面加载时出发的事件,请看大神博客,以上例子改自此博文。
以上,就是《Javascript高级程序设计》第二章的学习笔记。