不加async或defer的script放在head会阻塞html解析和渲染;浏览器流式解析时遇此类脚本即暂停dom构建,导致白屏、document.getelementbyid返回null等错误。

script 放在 head 里不加属性,页面就卡住
浏览器解析 HTML 是流式进行的,遇到没加 async 或 defer 的 <script src="xxx.js"></script>,会立刻暂停 DOM 构建,等脚本下载、解析、执行完才继续。哪怕只是 2KB 的空文件,首屏白屏也几乎是必然的。
常见错误现象:document.getElementById('app') 返回 null;按钮点击无响应; 内容迟迟不渲染。
- 绝对不要把未加加载策略的外部脚本(尤其是 jQuery、Vue、React 初始化脚本)直接扔进
-
document.write()必须禁用——现代浏览器已废弃,本地双击打开 HTML 时会清空整个文档流,必白屏 - 内联脚本(
<script>console.log(1)</script>)加async或defer无效,HTML5 规范不支持
该用 async 还是 defer?看脚本是否操作 DOM
不是“哪个更先进”,而是脚本是否依赖 DOM 节点、是否需要按顺序执行。
async 适合完全独立、不操作 DOM、无顺序要求的脚本:
- 典型场景:
analytics.js、广告 SDK、错误监控脚本 - 多个
async脚本谁先下完谁先执行,顺序不可控;若 A 依赖 B 定义的函数,大概率报ReferenceError - 它可能比
还早执行,所以别把它放在底部——反而容易因 DOM 未就绪而出错
defer 适合主业务逻辑、需操作 DOM、有执行顺序依赖的脚本:
- 典型场景:初始化 Vue 实例、注入首屏数据、绑定全局事件
- 多个
defer脚本严格按 HTML 中书写顺序执行,且一定在 DOM 解析完成、DOMContentLoaded触发前执行 -
defer对内联脚本无效;type="module"脚本默认自带defer行为,无需重复加
script 放哪儿最安全?关键不在位置,在加载策略
只把 <script src="app.js"></script> 移到 前,并不能彻底解决问题——JS 下载期间仍会阻塞后续 HTML 解析,DOMContentLoaded 时间仍可能被拖长。
- 兜底方案:放
前 + 显式加defer,既保 DOM 就绪,又控执行顺序 - 更优方案:统一用
defer+ 外链,或改用type="module",天然支持顶层await和静态分析 - 混用风险高:一个页面里同时存在
async和defer脚本,且后者依赖前者导出的变量,运行时大概率失败 - 跨域脚本(如 CDN 上的 JS)用了
defer却漏掉crossorigin属性,会导致错误堆栈丢失,调试困难
哪些脚本根本不该在首屏加载
优化的本质是「延迟非关键」,而不是压缩或合并。很多脚本根本不需要用户一打开页面就执行。
- 第三方统计/埋点脚本:用
async是底线,更推荐监听首次click或scroll后再动态插入<script src="analytics.js"></script> - 非首屏模块 JS(如评论区、分页器):初始不加载,等用户滚动到视口再通过
import()动态导入 - 大体积工具库(如
lodash、moment):确认是否真被用到;若只用几个函数,优先改用date-fns或lodash-es按需 import - CDN 提供的 ES 模块地址必须带
+esm后缀(如 jsDelivr),否则返回 CommonJS 格式,浏览器报错Cannot use import statement outside a module
写了 defer 不代表一定生效;没报错也不代表没拖慢首屏。验证方式:Chrome DevTools → Network 面板,找 JS 请求的 Initiator 列 —— 若是 parser,说明仍是同步加载;应为 other 或空。执行时机用 console.timeStamp('script-start') 对比 DOMContentLoaded 时间戳,才能确认是否落在预期区间。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











