是的,script不加属性放head中必然阻塞首屏渲染;应按依赖关系选async(无依赖、无序执行)或defer(有dom依赖、保序执行),严禁同步脚本置于head。

script 标签不加属性就扔进 ,首屏白屏几乎是必然的——浏览器会立刻暂停 HTML 解析,等 JS 下载并执行完才继续。
怎么判断该用 async 还是 defer
关键不是“哪个更先进”,而是脚本有没有 DOM 依赖、是否需要按顺序执行。
-
async:适合完全独立、无依赖、执行时机无所谓的小脚本,比如analytics.js、广告 SDK;它下载时不阻塞,但一加载完就立刻执行,可能早于构建完成,此时document.getElementById会返回null -
defer:适合业务主逻辑,比如初始化 Vue 实例、注入首屏数据;它下载不阻塞,执行严格在 DOM 解析完成后、DOMContentLoaded前,且多个defer脚本按书写顺序执行 - 混用风险:一个
async脚本依赖另一个defer脚本里的函数,大概率报ReferenceError;defer对内联脚本无效:<script defer>console.log('hi')</script>会被忽略
script 放哪儿最安全
没有 async 或 defer 的 script,必须移出 ——这是底线。
- 兜底方案:放在
前,至少能保证 DOM 已构建完毕,但无法避免 JS 下载时的解析暂停 - 更优方案:统一用
defer+ 外链,既免去手动挪动位置的麻烦,又保留执行顺序控制权 - 绝对禁用
document.write():现代浏览器已废弃,执行即清空整个文档流,本地双击打开 HTML 时尤其敏感,必白屏
哪些脚本根本不该直接加载
不是所有 JS 都值得在首屏加载。优化的本质是「延迟非关键」,而不是「压缩或合并」。
- 第三方统计/埋点脚本:用
async,但建议进一步封装为「用户交互后才加载」,比如监听click或scroll后再动态插入<script src="analytics.js"></script> - 非首屏模块 JS(如评论区、分页器):初始不加载,等用户滚动到对应区域再通过
import()动态导入 - 大体积工具库(如
lodash、moment):确认是否真被用到;若只用其中几个函数,优先改用轻量替代(date-fns、lodash-es按需 import)
容易被忽略的细节和验证方式
写了 defer 不代表一定生效;没报错,也不代表没拖慢首屏。
-
crossorigin属性常被漏掉:当脚本跨域(比如 CDN 上的 JS)且用了defer,不加crossorigin会导致错误堆栈丢失,调试困难 - 验证是否真不阻塞:Chrome DevTools → Network 面板,找该 JS 请求的
Initiator列;如果是parser,说明仍是同步加载;应为other或空 - 检查执行时机:在脚本开头加
console.timeStamp('script-start'),对比DOMContentLoaded时间戳,确认是否落在预期区间
真正卡住首屏的,往往不是 JS 文件有多大,而是它被什么时机、以什么方式加载——一个没加 defer 的 5KB 统计脚本,比一个 100KB 但正确 defer 的业务脚本更伤性能。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











