HTML 中 script 标签的属性和加载顺序

上周有个客户问我,为啥他网站加了个async脚本后,某些DOM操作出问题了。
这让我想起搞懂script标签的属性和加载顺序确实挺重要的,直接关系到页面能不能正常跑。

你想想啊,默认情况下script是阻塞加载的,浏览器看到script标签就直接暂停解析,等脚本下载完再执行。
要是你把script放head里,那整个页面渲染都得等它跑完。
我之前做项目的时候,就见过一个老代码,把关键逻辑写在外部script里还用defer,结果页面白屏等了半天,因为加载顺序没控制好。

现在常用的属性就两个,async和defer,它们完全不一样:
async属性是啥呢?它就像个绕过阻塞的小偷——浏览器一边解析页面,一边偷偷加载这个script。
下载完立马就跑,不管DOM是不是建好了。
我2 02 3 年在上海某商场做一个电商网站,为了加快首屏显示,把统计js用async加载,结果用户反馈DOM还没挂好就执行了,导致页面闪烁。
所以async适合那种不需要等DOM、也不依赖DOM的脚本,比如广告、统计这些。

defer就不一样了,它是等整个DOM都解析完毕,再按顺序执行所有defer script。
多个defer script会按照你在里写的顺序来,不会打乱。
我在深圳帮一个金融客户改代码时,把所有依赖DOM的库都换成defer,页面性能直接提升了3 0%。
但要注意,defer script里不能依赖async script,顺序很重要。

还有一个type属性,现在基本不用了,默认就是text/javascript。
以前4 要写type="text/javascript"才认,现在浏览器都智能了。

总结一下我的踩坑经验: 1 . 复杂的第三方库用defer,简单的外部脚本用async 2 . 别指望async和defer的脚本按你写的顺序跑(async肯定乱,defer按顺序) 3 . 如果script放body最后,页面会更快显示,但defer script会等所有资源都解析完,可能延迟更大
反正你看着办吧,每个场景需求不一样。
我还在想那个统计脚本能不能用postMessage跨域执行...

这堆代码是百度知道网站的JavaScript和CSS资源加载脚本。
2 02 3 年,北京,代码量超过1 000行。
问题在于,这些脚本加载缓慢,影响了用户体验。
解决方法:优化脚本,减少依赖,使用异步加载。

html如何引用js

啊,HTML里放JavaScript啊,主要有俩法子。

第一,内联脚本。
就是直接在HTML里加个< script >标签,把JS代码写里面。
比如这样: < script > console.log("内联脚本执行"); </ script > 这玩意儿好处是啥呢?代码跟HTML混在一块儿,看一眼就懂。
适合写点简单的逻辑,比如一小段提示信息。

但缺点也挺明显。
这代码是在HTML解析的时候同步执行的,啥还没加载完呢,它就跑来执行了,可能把页面渲染给耽误了。
而且,这玩意儿不方便维护,也不容易复用。
你写个几十行的逻辑,每次都得复制粘贴,还容易出错。

第二,外部脚本文件。
这个就是把JS代码单独写个.js文件,然后在HTML里用< script >标签加个src属性指向它。
比如: < script src="script.js" > </ script > 这样,代码就分离出去了。
好处是啥?好维护啊,一个文件管一段逻辑。
想改代码,直接去.js文件里改就行。
还能复用,别的页面也能用这个脚本。

但加载时机要注意。
默认情况下,浏览器看到< script >标签,会先停下来加载这个js文件,等加载完了再继续解析HTML。
这会阻塞页面渲染,特别是js文件很大的时候,用户看着页面白屏等得着急。

为了优化性能,可以加个defer属性。
这样浏览器会等HTML解析完了,再去执行这个脚本。
顺序是按< script >标签出现的顺序执行的。
比如: < script src="script.js" defer > </ script > 或者用async属性。
这个是异步加载,浏览器不管你解析到哪儿了,只要js文件下载完了,就立马执行。
但执行顺序是不保证的,可能第一个加载完的先执行,也可能第二个先执行。

最佳实践呢?简单逻辑,比如一小段提示信息,就用内联脚本。
复杂点的,或者代码多的时候,都用外部文件。
要把< script >标签放在<body>标签闭之前,这样浏览器先把页面内容展示出来,再慢慢加载JS,用户体验好点。
对外部脚本,能用defer就别用async,特别是统计那些,要按顺序执行。

现在开发,很多人用ES6 模块或者Webpack这些工具来管理JS,就不多说了。
总之,怎么用要看情况,平衡好维护性和性能就行。