浏览器在执行JavaScript时,会出现阻塞,不可以做其他事情。因为浏览器使用单一进程来处理用户界面(UI)刷新和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 src="./file1.js"></script>
<script src="./file2.js"></script>
<script src="./file3.js"></script>
</head>
<body>
</body>
</html>
在head里加载了三个js文件,由于脚本会进行阻塞。所以直到这三个文件都加载完毕之后浏览器才会进行页面的渲染。也就是说在JS文件加载完成之前,整个页面都是空白的。
解决方案:
1、把script标签放在body闭合标签之前
确保脚本执行之前页面已经完成了渲染
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./file1.js"></script>
</body>
</html>
2、利用script标签扩展属性(defer async)
带有defer属性的script标签可以放置在文档任意位置。对应的script标签会在页面解析到当前标签时开始下载,但并不会执行,直到dom加载完成(执行时机在onload事件被触发之前)。不会阻塞浏览器其他进程。与其他资源并行(异步加载)下载。
async 属性与defer属性加载方式相同,都是异步加载,不同的是async是HTML5规范中添加的属性。并且两者执行机制也不相同,async是文件加载完毕之后自动执行,defer是在页面load事件触发之前。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script defer>
alert('2')
</script>
<script>
alert('1')
</script>
<script>
window.onload = function(){
alert('3')
}
</script>
</body>
</html>
3、动态脚本元素
可以利用JavaScript动态创建标签的形式加载js文件。(异步加载,加载完毕立刻执行)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var script = document.creatElement('script');
script.type = 'text/javascript';
script.src = 'file1.js'
document.getElementsByTagName('head')[0].appendChild(script);
</script>
</body>
</html>
注意 :js中如果某些逻辑对于此文件有依赖时,必须确保此脚本下载完成。这个时候我们可以通过load事件对于此标签的加载做监听。注意IE中并不支持load事件的方式。但是IE会触发readystatechange事件。可以写一个兼容两者的函数。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function loadScript(url, callback) {
var script = document.creatElement('script');
script.type = 'text/javascript';
script.src = url
document.getElementsByTagName('head')[0].appendChild(script);
if (script.readyState) {
script.onreadystatechange = function(){
if (script.readyState === 'loaded' || script.readyState === 'complete') {
// 注意IE在判断最终状态readystate的值时并不准确,需要同时检查这两种状态
script.onreadystatechange = null;
callback();
}
}
} else {
script.onload = function() {
callback();
}
}
}
</script>
</body>
</html>
4、利用XMLHttpRequest
可以采用XHR的异步模式。使脚本进行异步下载。
优点:
1、可以下载完成JavaScript文件但不执行。
2、 兼容性较好。
缺点:
JavaScript文件必须与所请求的页面在相同的域下,否则会产生跨域。但是这样的话就不能使用CDN的方式。所以大型的JavaScript应用并不会采用XHR的方式。
var xhr = new XMLHttpRequest();
xhr.open('get', 'file1.js', true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
var script = document.creatElement('script');
script.type = 'text/javascript';
script.text = xhr.responseText;
document.body.appendChild(script)
}
}
}