首屏白屏主因是脚本阻塞解析,非位置问题;必须用defer(保序、依赖dom)或async(无依赖、乱序执行),type="module"更优;内联脚本永远阻塞,需改用module动态导入。

脚本阻塞解析是首屏白屏的主因,不是位置问题,而是是否声明加载策略——defer和async必须用,type="module"更优,内联脚本永远阻塞。
script 标签加 defer 还是 async?看依赖关系
两者都让下载不阻塞 HTML 解析,但执行时机和顺序逻辑完全不同。
-
defer:适合有 DOM 依赖、且需保序的初始化脚本(如轮播图、表单校验),执行在DOMContentLoaded前,按书写顺序 -
async:适合无依赖、可并行执行的独立脚本(如统计 SDK、广告代码),谁先下完谁先跑,顺序不可控 - 混用
defer和async在同一页面会破坏执行时序,比如b.js(async)可能在a.js(defer)还没开始下载时就执行完毕,导致ReferenceError: init is not defined - 现代项目优先用
type="module",它默认等效defer,还支持静态导入分析、预加载提示、顶层 await
关键 CSS 和 JS 怎么配合才能不白屏
白屏时间长,往往不是 JS 慢,而是关键 CSS 加载被 JS 拖累——尤其是把 <link rel="stylesheet"> 放在 <script></script> 后面时。
- 关键 CSS 必须内联进
,哪怕只有 2KB,也能避免 FOUC 和首次渲染延迟 - 非关键 CSS 用
<link rel="preload" href="non-critical.css" as="style">+onload动态切换rel,确保复用且不阻塞 - 如果用了
defer脚本,<link rel="stylesheet">可放心放在它前面——浏览器会并行下载 CSS 和 defer 脚本,互不干扰 - 千万别写成:
<script src="app.js" defer></script><link rel="stylesheet" href="main.css">,这样 CSS 会等 JS 下载完才开始请求
loading="lazy" 加了反而更卡?检查这三处
loading="lazy" 是浏览器原生懒加载,但用错位置或缺失配套属性,会直接拖垮 LCP 和 CLS。
- 首屏图片绝对不能加
loading="lazy"——浏览器跳过加载,LCP 延迟数秒是常态 - 所有
<img>必须带width和height(或用aspect-ratiofallback),否则加载时尺寸突变,触发 CLS - SSR 页面中由 JS 动态插入的
<img>默认是loading="eager",要显式设为loading="lazy"才生效 - 对
<iframe></iframe>也适用同规则;但<video></video>不支持该属性,得用preload="none"
内联脚本为什么永远阻塞?怎么绕过
内联脚本(如 <script>init();</script>)没有 URL,浏览器无法异步下载,只能同步解析+执行,不管放 还是 前都会卡住 DOM 构建。
- 唯一绕过方式:改用
type="module"内联,例如<script type="module">import('./init.js');</script>,它自动获得 module 级别异步能力 - 极小逻辑(如设置 class、跳转)可保留内联,但禁止调用未声明函数、禁止多行、禁止含
字符串(会提前闭合标签) - 构建工具能自动把内联脚本外链化并加
defer,比手写更可靠 - 服务端渲染(SSR)场景下,内联初始化数据建议用
<script id="__NEXT_DATA__" type="application/json"></script>这类纯数据 script,不执行逻辑
最容易被忽略的是:HTML 结构顺序本身就在参与渲染调度——首屏内容越靠近 开头,浏览器越早开始绘制;而一个没加 defer 的 <script src="vendor.js"></script> 放在 顶部,足以让整个首屏等待数秒。性能优化不是堆技巧,是让每个标签都清楚自己该在什么时候、以什么方式参与渲染流水线。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











