预解析扫描器是浏览器在主解析器被阻塞时立即启动的轻量后台线程,仅扫描初始html字节流中静态资源标签(如、、),提前发起并行下载;不执行js、不构建dom、不识别动态插入或内联脚本,且受限于dom深度≤6层及首层直出要求。

预解析扫描器(Preload Scanner)不是“等主线程空闲再干活”,而是浏览器在 HTML 主解析器被同步脚本或样式阻塞的间隙,立即启动的一个轻量级后台线程——它不执行 JS、不构建 DOM、也不渲染,只做一件事:快速向前扫描尚未解析的 HTML 字节流,提前发现并发起关键资源的网络请求。
它怎么“抢时间”下载外链资源
当主解析器遇到 <script src="a.js"></script> 且无 async/defer 时,会立刻暂停 DOM 构建,去下载、编译、执行该脚本。此时主线程卡住,但预解析器就趁这个“停顿窗口”启动,从当前标签位置往后扫描,找到 <script src="b.js"></script>、<link rel="stylesheet" href="main.css">、<img src="hero.jpg"> 等静态声明,立刻发起并行下载。这些请求和 a.js 的执行完全不冲突,也不依赖 a.js 执行完成。
它能看见什么、不能看见什么
- ✅ 能识别:
<script src="..."></script>(含 async/defer)、<link rel="stylesheet">、<img src="...">、<link rel="preload" as="script">—— 只要出现在初始 HTML 字节流中,且路径是静态字符串 - ❌ 完全无视:
document.createElement('script')、innerHTML = '<img src="...">'、JSX 或模板语法生成的标签、<script>fetch('x.js')</script>、@import规则里的 CSS —— 这些都属于运行时动态行为,预解析器不执行 JS,也看不到它们 - ⚠️ 有限识别:
<picture></picture>内的<source></source>可被发现,但如果整个<picture></picture>被包在<div class="lazy"> 里,预解析器会在深度超 6 层或遇到惰性容器(如 <code><details></details>)时主动截断,跳过不看为什么它对性能影响巨大
关键在于“下载”和“执行/解析”是解耦的。预解析器让浏览器把本该串行做的事变成并行:HTML 解析器还在等 a.js 执行完,b.js、main.css、hero.jpg 已经在后台下载了。实测在弱网下,首屏核心资源(如 main.js)通过
<link rel="preload" as="script">提前 400–800ms 发起请求,比写成<script src="main.js"></script>起始下载早得多——因为后者必须等到 HTML 解析到那一行才触发,而那时带宽可能已被其他资源占用。写法上最容易踩的坑
-
<script async>console.log(1)</script>:内联脚本不被预解析,async 属性无效,它会立刻同步执行并阻塞解析,还导致预解析器暂停 -
<link rel="preload" href="font.woff2">漏写as="font":浏览器无法识别资源类型,降级为低优先级请求,Network 面板显示 Priority=Low -
<link rel="preload" href="main.js" as="script">放在后半段:它必须紧贴<meta charset>后、<title></title>前,否则解析器读到它时已错过最佳抢占时机 - 用
fetchpriority="high"但放在条件渲染块里(如v-if="loaded"):预解析器只扫首层直出 HTML,JS 控制的结构它根本看不见
-











