前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库preload scanner 预加载扫描器 推测解析
浏览浏览器渲染与调度

HTML 解析器被脚本阻塞时,浏览器的预加载扫描器为什么还能提前发起资源请求?

主解析器遇到同步脚本会暂停,但预加载扫描器是独立的轻量推测扫描通路,继续向前扫描剩余 HTML,提前发现资源引用并发起网络请求,与主解析器并行工作。

前端进阶之旅 · 一题精讲更新于 2026.09.05
浏览器#渲染与调度
先看核心答案
理解线索

两条解析通路的分工

  1. 主解析器构建 DOM,遇同步脚本暂停,保证语义正确
  2. 预加载扫描器只扫字节流找 src/href,直接发起请求
  3. 推测而非承诺扫错也无害,主解析器走漏的资源会补发

只认 HTML 字面量里的 URL,JavaScript 动态生成的引用不在其视野内。

核心回答

先记住这个答案

主 HTML 解析器遇到不带 async/defer 的 script 时必须停下:先下载再执行,因为脚本可能 document.write 改写文档。但浏览器另起一个预加载扫描器,它只做推测性词法扫描,不构建 DOM、不执行脚本、不处理条件注释,只从原始字节里识别 src、href 这类可提前请求的资源标签,如 script、link、img,然后直接交给网络栈排队下载。这样等主解析器恢复并走到该标签时,资源往往已在传输甚至已就绪。它是纯优化,即使扫描器漏掉某个资源,主解析器走到那里仍会正常请求,只是更晚。

  • 预加载扫描器独立于主解析器,只做推测扫描
  • 只识别标签里的静态资源引用,不执行脚本
  • JS 拼接的资源 URL 扫描器看不到
  • 被提前请求的资源省去的是发现延迟

为什么阻塞主解析器却阻塞不了扫描器

主 HTML 解析器承担 DOM 构建,遇到同步 script 必须暂停:脚本可能通过 document.write 改变后续文档结构,浏览器必须先下载并执行它才能保证解析结果正确。这段等待期间网络连接是空闲的,而文档后续字节往往已经在缓冲区里。

预加载扫描器是浏览器在同一文档上跑的第二条轻量通路:它对剩余字节做推测性词法扫描,不做树构建、不执行任何脚本、不求值条件分支,只匹配 script src、link href、img src 这类资源引用,把发现的 URL 直接提交给网络栈。两条通路读同一份字节流但互不等待,所以主解析器被脚本卡住时,扫描器照样把后面的 CSS、字体、脚本提前送上网络。

首屏有同步第三方脚本时的加载对比

假设某页面 <head> 第三个标签是同步的统计脚本,其后还有 styles.css、web 字体和首屏 hero.jpg。统计服务器 TTFB 约 800ms,期间主解析器完全停在该 script 上。若没有扫描器,CSS、字体、图片要等 800ms 后才被发现,首屏渲染预期会整体后移近一秒。

实际行为是:HTML 字节到达后预加载扫描器立即向后扫,在统计脚本还没返回时就把 styles.css、字体和 hero.jpg 的请求发出。等统计脚本执行完、主解析器走到 link 标签时样式表可能已下载完毕,可直接进入 CSSOM 构建。开发者可在 DevTools Network 瀑布图中观察到这些资源与统计脚本几乎同时起求,而非串行排在它后面。

扫描器看不到的资源与补救手段

扫描器只理解 HTML 字面量,不解析 CSS 内容,因此两类引用无法提前发现:一类是脚本运行时动态生成的 URL,例如用 new Image() 预取、通过代码拼接 URL 后插入 link;另一类写在 CSS 内部,例如用 url() 引用的背景图,要等对应 CSS 下载解析后才暴露。前者的发现时间取决于脚本何时执行,后者取决于样式表何时解析。

对这类资源可用 <link rel="preload"> 显式声明,浏览器读到提示即发起请求,不再依赖扫描器发现。代价是需要准确的 as 属性和可选的 crossorigin,声明错类型会导致重复下载;对扫描器已能及时发现的静态标签再加 preload 收益有限,主要可能改变其请求优先级。

推测扫描失效或被削弱的条件

第一类失效是内容不可见:资源 URL 由脚本拼接、藏在 template 内容由脚本实例化、或位于需要条件求值的分支里,扫描器无法判断是否会真正用到,只能放弃。第二类是请求本身发不出:目标域名尚未解析、连接数打满、或被 Content-Security-Policy 拦截,提前发现不等于提前拿到。

还要注意扫描器只解决发现时机,不解决带宽竞争:同步脚本阻塞期间扫描器同时发出的请求会共享网络,慢请求照样拖住后续渲染。可操作判断是:在瀑布图中某资源起始时间明显晚于 HTML 到达时间且它是首屏必需,就该检查它是否藏在脚本后面,需要时改为静态标签或加 preload。

回答前,多想一步

容易答错的地方

扫描器会执行或部分执行脚本
错误。预加载扫描器只做词法层面的标签匹配,不解析 JavaScript、不构建 DOM、不执行任何代码。它能容忍猜测错误,因为主解析器才是语义的最终裁决者,扫描器漏掉的资源稍后仍会被正常请求。
扫描器能发现页面上的所有资源
错误。它只能识别 HTML 字节流中字面写出的 src/href。脚本动态插入的标签、JS 拼接的 URL、CSS 内部 url() 引用都不在其视野内,这些资源要等宿主代码执行或样式表解析后才会被发现。
试着用自己的话回答

面试官还会怎么问?

预加载扫描器和 preload 资源提示是什么关系?

扫描器是浏览器自动的推测机制,只覆盖 HTML 里静态写出的资源;rel="preload" 是开发者显式声明,能覆盖扫描器看不到的资源如字体、CSS 背景图。两者都让请求更早发出,但 preload 必须配对正确的 as 属性。

同步脚本都改成 async/defer 后扫描器还有用吗?

仍然有用。主解析器不再被脚本暂停,但 CSS、字体、图片仍按标签顺序被发现,扫描器仍能在主解析器到达之前提前发出这些请求,缩短发现延迟。它只是与脚本阻塞无关的另一个收益点。

扫描器提前请求的资源会被主解析器重复请求吗?

不会。请求进入统一的网络栈和缓存,主解析器走到对应标签时直接复用已在传输或已缓存的响应。例外是 preload 的 as 与实际使用类型不匹配,可能导致同一 URL 被当作不同类型资源重复下载。

从一道题,走向一组知识

把知识连起来

渲染与调度

document.readyState 的 loading、interactive、complete 三个值分别对应页面加载的什么时刻?

同属「渲染与调度」专题,接着看 document.readyState readystatechange 加载状态 在具体场景中的处理方式。

渲染与调度

DOMContentLoaded 事件与 window 的 load 事件分别等待什么资源,监听哪个会漏掉图片加载完成?

同属「渲染与调度」专题,接着看 DOMContentLoaded load 事件区别 资源加载 在具体场景中的处理方式。

参考资料

  • Populating the page: how browsers work

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 为什么阻塞主解析器却阻塞不了扫描器
  3. 首屏有同步第三方脚本时的加载对比
  4. 扫描器看不到的资源与补救手段
  5. 推测扫描失效或被削弱的条件
  6. 容易答错的地方
  7. 面试官还会怎么问
  8. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑