背景
最近开发的过程中遇到一个问题:页面中加载了一个改写fetch的js文件,然后后面使用了fetch发送请求。发现在实际上,并没有实现对fetch的改写,而是使用了原生的fetch。初步怀疑是由于js的加载顺序导致了问题的出现,也就是说在使用fetch的代码执行之前,改写fetch的代码还未加载执行完成,所以没有实现对它的改写。
实际上并不是因为这个,而是因为改写fetch的js文件放在另外一个系统文件中,而这个系统是需要登录的,但是在本地开发中,是不做登录验证的,即使没有登录也不会拉起CAS登录,所以文件加载失败了,改写也没有成功。
即使如此,还是需要补充一点关于script
标签加载js的知识。
基础知识
script
标签需要放在body最后,这是开发中一直坚持的习惯,为什么这样做呢?因为js的加载和执行会阻塞后续html的解析和渲染。如果一个script标签放在html中间,当执行到这一句时,会停止对html的解析和渲染,转而进行js的加载和执行,结束以后,再继续后面的内容。
defer和asnyc
1.defer
这个属性的用途是表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。因此,在<script>元素中设置defer
属性,相当于告诉浏览器立即下载,但延迟执行。
HTML5规范要求脚本按照它们出现的先后顺序执行,因此第一个延迟脚本会先于第二个延迟脚本执行,而这两个脚本会先于DOMContentLoaded
事件执行。在现实当中,延迟脚本并不一定会按照顺序执行,也不一定会在DOMContentLoad
时间触发前执行,因此最好只包含一个延迟脚本。
2.async
这个属性与defer
类似,都用于改变处理脚本的行为。同样与defer
类似,async
只适用于外部脚本文件,并告诉浏览器立即下载文件。但与defer
不同的是,标记为async
的脚本并不保证按照它们的先后顺序执行。
第二个脚本文件可能会在第一个脚本文件之前执行。因此确保两者之间互不依赖非常重要。指定async属性的目的是不让页面等待两个脚本下载和执行,从而异步加载页面其他内容。
3.对比
蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。
也就是说,对于默认的情况,当解析html过程中遇到了
script
,会停下对html的解析和渲染,去下载和执行js。当设置了
defer
后,当遇到script
时,会并行下载,等到html解析完成后,再按顺序执行这些js。当设置了
async
时,当遇到script
会并行下载,并且谁先下载完就先执行谁。但是需要注意的是,由于各个浏览器厂商对于规定的执行标准不同,其实并无法保证
defer
的情况下js一定是按顺序执行的,所以最稳妥的方法还是将js放到body
底部,等待所有html执行完毕再进行js的解析执行,避免出现执行顺序错误导致的问题。但是如果不考虑 执行顺序,使用defer
可以在解析html时并行加载js,应该是可以节省加载时间的。