Chromium 内核浏览器中不同类型的网络请求执行的优先级时如何的?
背景
书接上文,我从发起者视角把浏览器的网络请求分为了声明式和命令式。同时知道了所有网络请求最终都汇入同一条“河流”,即网络服务(Network Service)由同一发起线程发起和同一处理线程进行处理。
说到这里,我又有问题了。既然大家都殊途同归,那它们的执行优先级又是如何的呢?
资源优先级与调度
我们都知道浏览器如何决定先加载哪个资源,对页面性能至关重要因为资源的加载顺序大大影响着页面最终呈现给用户的速度。但这里并没有统一标准规范,这是一个“约定优于配置”和“开发者控制”并存的领域。基于这两块领域,目前有两种手段影响资源的优先级和调度:
浏览器的默认启发式策略: 利用浏览器内部复杂的启发式算法来为不同资源分配优先级。
-
关键资源优先: 用于渲染页面的关键
CSS(<link rel="stylesheet">)和阻塞性的JavaScript(<script>无 async/defer)通常拥有 Highest(最高)或 High(高)的优先级,因为它们会阻塞页面的渲染。 -
fetch 请求: 通过
JavaScript发起的fetch调用通常也被赋予High优先级,因为它们往往是应用逻辑获取关键数据所必需的。 -
图片的复杂情况:
<img>标签是一个有趣的特例。默认情况下,它们的优先级是 Low(低)。但浏览器会动态调整: 例如,Chrome 117 及更高版本会将页面中前 5 个大尺寸图片的优先级提升至 Medium(中等),而一旦某个图片进入了用户的可视区域(viewport),其优先级会被立刻提升至 High。这些动态变化都可以在 Chrome 开发者工具的“网络”(Network)面板中观察到。
开发者的控制手段: 当浏览器的默认行为不符合预期时,开发者可以通过一些手段进行干预。
-
<link rel="preload">: 这是一个发现提示,而非严格意义上的优先级提示。它告诉浏览器: “这个资源未来肯定会用到,请尽早开始获取它”,从而让浏览器可以提前发现那些隐藏在 CSS 或 JS 深处的资源。但是,被预加载的资源仍然以其类型的默认优先级进行获取(例如,预加载的图片默认还是 Low 优先级)。 -
fetchpriority 属性: 这是一个显式的调度提示,是开发者干预资源优先级的最强力工具。它允许开发者为
<img>、<link>和<script>标签设置fetchpriority="high"或fetchpriority="low",从而直接覆盖浏览器的默认优先级。这对于优化核心 Web 指标(Core Web Vitals)中的最大内容绘制(LCP)至关重要。通过为 LCP 图片设置fetchpriority="high",开发者可以确保该图片从一开始就以高优先级下载,而无需等待浏览器启发式地提升其优先级。
安全层面的考虑
当我们使用 XHR 或 Fetch 进行网络请求时,需要特别注意跨域安全问题。针对跨域对于声明式请求我们好像从来没考虑过,但实际上服务端网关层会处理这种问题,前端确实是不管的。
上面说了一大通好像都只是在说声明式和命令式请求之间的差异只是发起端不一样而已,但是声明式请求与命令式请求之间最重要、最能体现设计意图的差异之一就是安全层面的设计考虑。下面我就来详细讨论一下两种请求的安全层面的差异:
-
声明式标签(
<img>、<script>等)的默认行为: 当这些标签请求一个跨域资源时,它们默认发起的是一种no-cors请求。这是一种高度受限的模式,其安全原则是“仅供使用,不可读取”。浏览器会正常接收并使用资源(如渲染图片、执行脚本),但会严格禁止页面中的 JavaScript 代码访问响应的任何内容(包括响应体和大部分响应头)。对于图片,这意味着你可以展示它,但无法在<canvas>中使用它而不“污染”(taint)画布。对于脚本,这意味着如果脚本执行出错,全局的onerror事件处理器无法捕获到详细的错误信息,只能得到一个模糊的Script error.。这是一种为简单嵌入场景设计的“默认安全”模型。 -
命令式 API(fetch, XHR)的默认行为:
fetch API默认发起的是cors请求。这种模式是为 API 通信而设计的。对于跨域请求,浏览器会遵循完整的跨源资源共享(CORS)协议。对于可能产生副作用的“非简单请求”(如 PUT、DELETE 或带有自定义头的 POST),浏览器会自动发送一个 OPTIONS 预检(preflight)请求,询问服务器是否允许即将到来的实际请求。只有当服务器在响应中返回了正确的Access-Control-Allow-Origin等 CORS 头部时,浏览器才会将响应暴露给 JavaScript 代码。XMLHttpRequest也遵循同样的行为。 -
crossorigin 属性: 权限提升的开关:
<img>和<script>等标签上的crossorigin属性,是连接上述两种安全模型的桥梁。它允许开发者显式地请求提升请求的权限。设置crossorigin="anonymous"会告诉浏览器将请求模式从默认的no-cors切换到cors,并发起一个不携带身份凭证(如 Cookie、客户端证书)的 CORS 请求。设置crossorigin="use-credentials"则会发起一个携带身份凭证的 CORS 请求。如果服务器端也配置了允许 CORS 请求,那么这次“权限提升”就成功了。成功后,跨域图片就可以被<canvas>安全地读取,跨域脚本的详细错误信息也可以被捕获。
总结
最后用一个表格总结一下 Chromium 中部分资源的默认优先级以及 fetchpriority 属性的影响:
| 特性 | 发起上下文 | 治理标准 | 设计意图 | 默认 CORS 模式 | CORS 模式切换 | 默认优先级 | 优先级控制 | 缓存控制 | 响应访问 |
|---|---|---|---|---|---|---|---|---|---|
<img> 、 <video> | HTML 解析器 | WHATWG HTML | 简单、声明式地嵌入媒体内容 | no-cors | crossorigin | Low (常被启发式策略提升) | fetchpriority、 loading="lazy" | 仅 HTTP 响应头 | 脚本不可访问 (对脚本透明) |
<link> 、 <script> | HTML 解析器 | WHATWG HTML | 链接关键的渲染或逻辑资源 | no-cors | crossorigin | Highest (CSS), High/Medium (Script) | fetchpriority、 async、defer | 仅 HTTP 响应头 | 脚本不可访问 (对脚本透明) |
XMLHttpRequest | JavaScript API 调用 | WHATWG XHR | 用于动态页面的异步数据交换(AJAX) | same-origin | 浏览器根据 URL 自动处理 | High | 不可用 | HTTP 响应头, URL 参数“缓存破坏” | responseText, responseXML 等属性 |
fetch | JavaScript API 调用 | WHATWG Fetch | 一个现代、强大、统一的底层网络交互构建块 | cors | mode 配置项 | High | priority 配置项 | HTTP 响应头 + cache 配置项 | 返回解析为 Response 对象的 Promise |
参考阅读
-
https://developer.mozilla.org/zh-CN/docs/Web/HTML/Reference/Attributes/rel/preload
-
https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLLinkElement/fetchPriority
-
https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
-
https://developer.mozilla.org/zh-CN/docs/Web/HTML/Reference/Attributes/crossorigin