这是我参与「掘金日新计划 · 2 月更文挑战」的第 2 天
高性能JavaScript-加载与执行
JavaScript是一门单线程的语言,在一段时间内只能执行一个脚本文件,无论该脚本是内嵌还是外链的,这就意味着该脚本的执行会阻碍后续流程,浏览器页面的下载和渲染也会往后延迟,如页面之中加入document.write(),显示广告。
<script type="text/javascript">
documenrt.write("The date is" + (new Date().toDateString()))
</script>
当浏览器遇到javascript代码的时候,浏览器会优先执行javascript代码,停止解析与渲染页面,这个时候,会造成后续流程阻塞,同样对于src标签引入的javascript代码,浏览器也会优先下载并且解析,在这个过程之中页面的渲染和页面与用户之间的交互是完全被阻塞的。
那么有什么原生JS方法解决办法解决这一难题吗,我们通常采用如下方法进行解决,实现一个无阻塞代码
延迟的脚本
HTML4为script标签定义了扩展属性defer,这个扩展属性指明javascript代码不会修改DOM,DOM不会被修改,页面元素不会被修改,脚本代码会延迟执行。但是该属性只会被IE 4+firefox3.5+的浏览器支持,在其他版本的浏览器这几种和,defer属性会被忽略。
我们需要注意的是:一个带defer属性的标签,无论它是内嵌或者外链的形式,它都会在DOM完成记载之前执行
<script>
window.onload = function() {
alert("load")
}
</script>
<script defer>
alert("defer")
</script>
<script>
alert("script")
</script>
如果是支持defer属性的浏览器,上面代码的执行顺序是"script","defer", "load",如果是不支持defer属性的浏览器,执行顺序是"defer","script","load"。
动态脚本元素
script标签与HTML页面中的其他元素一样,都能被DOM引用,可以在文档中被移动删除创建,可以在文档之中动态创建脚本,引入下载外链脚本元素。
var script = document.createElement("script")
script.type = "text/javascript"
script.src = "引入文件链路:https开头"
document.getElementsByTagName("Name")[0].appendChild(script)
只有当该创建的script元素被添加到页面中去,脚本才会被下载,并且该脚本的下载与执行不会阻碍页面中其他进程,也可以把创建的script元素放入head之中
XMLhttpRequest脚本注入
使用XHR对象获取脚本并且注入页面,先创建一个XHR对象,下载JavaScript文件,最后动态创建元素script将代码注入页面之中。
let xhr = new XMLhttpRequest();
xhr.open("get","文件地址", true);
xhr.onreadystatechange = function() {
if( xhr.readyState == 4) {
if( xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
let script = document.createElement("script");
script.type = "text/javascript";
script.text = xhr.responseText;
docuemnt.body.apendChild("script");
}
}
}
xhr.readyState表示请求处理完毕,status == 2XX表示有效请求,status === 304表示请求从本地读取。 这种方法下载下来的代码可以立即执行,等到需要的时候再执行,可是它只能下载同域下的代码。不能从CDN下载代码,大型的Web通常不会采用这种方法。
小结
- 页面中脚本数目越小越好,加载越快,进行合并
- 通常推荐将script标签放在之前,页面的底部,可以确保页面在进行脚本在执行前页面已经渲染完成,这种方法适合脚本代码对页面DOM更改并不多的页面
- 无阻碍下载JavaScript
- 使用defer属性
- 动态创建script元素下载代码
- 使用XHR下面脚本注入页面