资源发现而非传输:通过HTTP/3预加载React应用
了解为何HTTP/3会导致在React应用中资源延迟加载成为真正的瓶颈,以及preloadModule、preinit、Early Hints和分块技术如何解决这一问题。
将服务器升级为HTTP/3虽能提升数据传输速度,但许多React应用的实际响应速度提升并不明显。通常的原因在于,流程中耗时的部分从来不是数据传输本身,而是浏览器得知某个资源存在的那一刻。本指南将这两个问题区分开来,指出QUIC真正能发挥作用的环节,并介绍那些有助于提前完成资源发现的工具:React 19的资源API、基于意图的模块预加载、103 Early Hints响应、流式服务端渲染以及适用于多路复用传输的分块策略。
表面看似正常但实际仍很慢的工作流程
想象一个团队通过4G连接来调试分析仪表板。所有关键数据块都已预加载,网络传输状态看起来很正常,但“交互时间”却依然远远低于目标值。每个数据块一旦开始下载就会很快完成,由此可见HTTP/3确实发挥了作用。
仔细观察会发现情况有所不同。虽然下载速度很快,但请求的发起时间却较晚。React需要先下载、启动、运行并渲染内容,直到达到懒加载的边界点,浏览器才会意识到需要Dashboard.js。在开始请求该数据块的第一个字节之前,已经过去了数百毫秒。这种延迟源于网络上游的某个环节——即大多数性能检查清单中从未单独提及的资源发现阶段,而非资源传输阶段。
资源传输与资源发现是两个不同的问题
将“加载资源”这一过程拆分为两个问题会有所帮助:
- 传输速度:一旦发出请求,数据字节从服务器传送到浏览器的速度有多快?
- 发现时间:浏览器究竟在何时意识到自己需要这些数据字节?
从样式表中引用的网页字体会让这种差异更加明显。其流程如下:
HTML → CSS → @font-face rule → font request
无论连接速度多快,只有在HTML文件到达、CSS被获取并解析,且找到@font-face规则并与渲染后的文本匹配之后,才能发起字体请求。这就存在一定的发现延迟。<link rel="preload">标签并不会让字体下载速度加快哪怕一毫秒,它只是能让浏览器更早地发出请求而已。本指南其余内容几乎都是对这一理念的不同阐述。
哪种工具能解决哪些问题
下面介绍的每种技术都针对加载流程中的不同阶段:
preconnect:在发起任何请求之前,浏览器应先与哪些源地址建立连接?preload:浏览器会哪些特定文件发现得太晚?
preloadModule 和 modulepreload:哪些 ES 模块代码段应在执行前被获取并编译?preinit 和 preinitModule:哪些资源不仅需要被获取,还必须尽早应用或执行?prefetch:在下次导航时可能需要的内容,但优先级较低。只有最后一项与传输相关,其余均涉及资源发现、时机控制或调度,而这些方面恰恰体现了其他优势通常所在的位置。
HTTP/2 多路复用的不足之处
在HTTP/1.1协议下,浏览器通过为每个主机建立多个TCP连接来实现并行处理,通常最多为六个,且每个连接一次仅能传输一个资源。HTTP/2则改用单个连接来交错传输多个数据流:
HTTP/1.1 HTTP/2
──────────────── ────────────────────
TCP conn 1 → JS One connection
TCP conn 2 → CSS ├── Stream A: JS
TCP conn 3 → Font ├── Stream B: CSS
TCP conn 4 → Image ├── Stream C: Font
└── Stream D: Image
这确实是一大进步,但HTTP/2依然基于TCP协议,而TCP只能保证单个字节流的严格有序传输。它无法区分哪些字节属于JavaScript数据流,哪些属于字体数据流。一旦有数据包丢失,TCP会暂停其后所有数据的传输,直到重传的数据到达,即便丢失的包只是C数据流的一部分,而其他三个数据流并未受到影响。
这就是TCP的头部阻塞现象。在网络状况良好的情况下这种问题很少显现;但在信号不稳定的移动网络上,它正是导致HTTP/2的多路复用功能无法完全实现其承诺的主要原因。
HTTP/3 下的 QUIC 变化
HTTP/3 用运行在 UDP 之上且能自行处理加密的 QUIC 替代了 TCP:
HTTP/2 HTTP/3
──────── ────────
HTTP/2 HTTP/3
↓ ↓
TCP QUIC
↓ ↓
TLS UDP
↓ ↓
IP IP
独立流消除了传输层中的 HOL 阻塞
由于 QUIC 在传输协议内部管理流,因此每个流都可以独立恢复。丢失的数据包仅会阻塞其所属的流:
Stream A ──────────────────── ✓
Stream B ──────────────────── ✓
Stream C ──────── X ─ retry
Stream D ──────────────────── ✓
A、B和D流持续传输,而C在等待重传。测量结果显示,在TCP性能最差的情况下效果最为显著。2025年7月发表的一项在六个国家开展的Catchpoint研究指出,在丢包严重的链路中,数据首次到达的时间中位数下降了41.8%。Wix的内部测试显示,连接建立速度提升了33%,p75 LCP指标也改善了20%。在稳定的宽带链路中,相比HTTP/2的优势仅约为5%。这种不对称性本身具有参考价值:性能提升集中在HOL阻塞问题严重的区域。请将这些数据视为相关研究的示例,而非适用于您流量的保证值,应自行对用户数据进行测试。
首次传输数据前的往返次数更少
通过基于TCP的HTTP/2,新建连接需要先进行TCP握手,再单独进行TLS协商,因此在任何应用数据开始传输之前要经历两次往返。而QUIC则将TLS 1.3的交换过程整合到连接建立阶段,仅需一次往返即可完成。对于重复访问的用户,0-RTT恢复机制允许加密后的请求数据与初始数据包一同传输。在150毫秒的跨洲链路中,这能为每次新建连接节省150到300毫秒的时间。需要注意的是,0-RTT数据可能会被重放,因此服务器通常只接受用于如获取静态资源这类可重试的请求。
能够跨越网络切换保持连接的机制
TCP通过源IP地址、目标IP地址和端口的组合来标识连接。当手机从Wi-Fi切换到蜂窝网络时,其地址会发生变化,从而导致TCP连接中断。而QUIC则使用不可见的连接ID,因此会话可以迁移到新的路径上。由于通勤列车驶离车站,并不会导致正在预取的路由数据块需要重新开始。
HTTP/3是否让预加载变得多余?
并非如此,理解其中原因正是整个话题的核心。HTTP/3优化的是资源的传输方式;而预加载优化的是资源请求的时机。二者作用于链条中的不同环节:
Browser
│
│ ← "I don't know I need this yet"
↓
Resource discovery ← preload operates here
│
↓
Request
│
↓
QUIC transport ← HTTP/3 operates here
│
↓
Server
传输协议无法获取那些尚未有人请求的内容。事实上,更快的传输速度反而会让延迟问题更加明显。假设某个数据块的传输时间从300毫秒降至80毫秒,原本部分隐藏在总时间中的400毫秒延迟现在则占据了绝大部分。瓶颈只是发生了转移,并未消失。
为何React要向浏览器隐藏依赖项
像<img>标签的源文件以及<link>标签的样式表这类普通HTML资源能够很早就被找到,因为浏览器的解析器(及其预加载扫描功能)在读取文档时就能发现它们。而由客户端渲染的React则需要在更长的处理流程之后,某些依赖项才会显现出来:
HTML → main.js → React executes → render → lazy() → discover Dashboard.js → download → render
使用 React.lazy() 时,导入操作会在 JavaScript 执行之后进行。在主代码包被下载、解析、编译并运行,且 React 渲染到足以处理懒加载组件之前,浏览器无法得知 Dashboard.js 的存在。如果是从速度较慢的手机首次访问,可能需要数秒时间才会开始请求该代码块。
const Dashboard = lazy(() => import("./Dashboard"));
// The browser has no idea Dashboard.js exists
// until this renders. And it only renders after
// React has fully bootstrapped.
Suspense 能改善等待时间,但无法加快组件加载速度
一个常见的误解是认为将组件包裹在 Suspense 中就能解决问题。实际上它并不会改变代码块请求的时间:
<Suspense fallback={<Loading />}>
<Dashboard />
</Suspense>
Suspense的作用在于协调:当懒加载组件尚未加载完成时,React会显示备用内容而非阻塞整个页面结构。这有助于提升用户体验质量,但并非用于预测资源加载情况的机制。请求仍会在同一较晚的时间点发起。之后HTTP/3可以高效地传输数据块,但它无法改变数据到达所需的时间。
React 19的资源API及其实际功能
React 19在react-dom中提供了一组函数,允许组件在渲染过程中意识到资源需求时,将相关提示传递给浏览器的调度器。这些函数并非简单的HTML标签封装:React会对它们进行去重处理,并在服务端渲染时将其插入文档头部,以便浏览器能更早地看到这些内容。
preconnect:预热资源地址
当跨源请求必定会很快发起时,可使用preconnect。它会在事先启动DNS解析、建立连接以及进行TLS握手。
import { preconnect } from "react-dom";
// Call this when you know a cross-origin
// request is coming - not just "might be coming."
preconnect("https://cdn.example.com");
仅将此功能用于那些必定会访问的资源地址。每条已预热的连接都会在客户端和服务器端消耗资源,而未被使用的连接则会被直接丢弃。
preload:提前获取特定文件
preload会指示浏览器开始下载某个已知的资源,但不会立即执行或应用该资源。字体就是典型的例子,因为否则它们会被CSS解析所掩盖:
import { preload } from "react-dom";
// Font hidden behind CSS - the browser won't
// find this until it processes @font-face.
// Preload surfaces it earlier.
preload("/fonts/inter.woff2", {
as: "font",
crossOrigin: "anonymous",
});
请注意crossOrigin: "anonymous"选项。字体始终以CORS模式请求,因此如果不设置该选项进行字体预加载,生成的请求将与实际请求不匹配,从而导致浏览器重复下载该文件。
preloadModule:获取并编译 ES 模块
preloadModule 对 ES 模块实现了相同的功能,而且更进一步:它会下载该模块、解析并编译它,然后将其保存在模块映射中,这样一旦有 import() 调用时就能立即使用该模块。
import { preloadModule } from "react-dom";
// Use this for lazy route chunks you know
// are likely to be needed soon.
preloadModule("/assets/Dashboard-abc123.js");
这非常适合那些很可能很快就会用到的延迟加载路由片段。
preinit 和 preinitModule:获取并投入使用
preinit 和 preinitModule 是功能更强的版本。它们不仅会获取资源,还会使其立即生效:样式表会被插入并应用,脚本在到达后会立即执行。
import { preinit } from "react-dom";
// You don't just want this downloaded -
// you want it applied before render.
preinit("/styles/app.css", { as: "style" });
对于 CSS 来说,这两个阶段之间的时间差最为重要。预加载的样式表虽然已被下载,但并未被应用。如果在该样式表在首次绘制之前就需要使用,即便提前进行了下载,渲染仍要等到有元素实际引入该样式表之后才会继续。preinit 能同时处理这两个步骤。而对于脚本,则需采取相反的谨慎态度:只有那些可以立即安全运行的 preinit 代码才适合使用。
根据用户操作触发 preloadModule
在启动时为每个路由都调用 preloadModule 会浪费带宽。最佳时机是在用户的操作意图显现之时,通常是指针移入某个导航链接,或键盘焦点落在该链接上,且就在点击之前的瞬间。
下面的组件负责实现这些功能。它会渲染一个普通的锚点,因此即使没有 JavaScript,链接依然可以正常使用;同时在 onMouseEnter 和 onFocus 事件中调用 preloadModule,以便键盘操作的用户也能受益;最终的导航功能则由 React Router 的 navigate 方法处理:
import { preloadModule } from "react-dom";
import { useNavigate } from "react-router-dom";
function NavLink({ to, chunkPath, children }) {
const navigate = useNavigate();
return (
<a
href={to}
onMouseEnter={() => preloadModule(chunkPath)}
onFocus={() => preloadModule(chunkPath)}
onClick={(e) => {
e.preventDefault();
navigate(to);
}}
>
{children}
</a>
);
}
// Usage
<NavLink to="/dashboard" chunkPath="/assets/Dashboard-abc123.js">
Dashboard
</NavLink>
鼠标悬停与点击之间的时间间隔通常在 100 到 400 毫秒左右。通过 HTTP/3,大小适中的数据块往往能在这一时间窗口内传输完成:以 10 Mbps 的速度,150 KB 的数据大约需要 120 毫秒。等到用户真正点击时,模块早已被编译到模块映射中,懒加载的边界问题也会立即得到解决,因此不会出现 Suspense 的降级处理。
onMouseEnter处理程序能够实现的是任何传输协议都无法做到的功能:它在发起导航请求之前,就将用户的操作意图转化为关于资源的实际信息。之后HTTP/3会高效地处理数据传输,各层各司其职。
有两点实际注意事项。首先,chunkPath必须是打包工具生成的哈希化文件名,因此在实际项目中应从构建清单中获取,而非手动输入。其次,触摸设备没有悬停功能,如果注重移动端导航体验,可以考虑使用onTouchStart或基于视口范围的触发机制。
打包工具级的预加载
对于在空闲时间进行大量低优先级预加载的操作,webpack支持在动态导入语句中添加特殊注释来实现:
const Dashboard = lazy(
() => import(/* webpackPrefetch: true */ "./Dashboard")
);
请注意,webpackPrefetch会生成一个<link rel="prefetch">提示,浏览器会将其视为在空闲时间进行的、针对未来可能发生的导航的低优先级任务。这与用于处理当前所需资源的高优先级preload或modulepreload信号不同。
Vite采用更自动化的方法,会为您自动生成modulepreload链接。而使用webpack则需要特殊的注释或插件。该提示的原始HTML格式如下:
<!-- Vite generates these for lazy chunks automatically -->
<link rel="modulepreload" href="/assets/Dashboard-abc123.js">
<link rel="modulepreload" href="/assets/vendor-react-def456.js">
严格来说,Vite 生成的 HTML 包含针对入口代码块及其静态导入的 modulepreload 链接。对于动态导入的代码块,Vite 的运行时辅助工具会在 import() 被调用的瞬间为这些依赖插入预加载链接,从而使代码块及其导入内容并行加载而非依次加载。具体行为可参考你所使用 Vite 版本的构建文档。
与普通的 rel="preload" 不同,modulepreload 能让浏览器在模块到达时立即解析并编译它,而无需等到执行时刻。通过 HTTP/3,多个此类提示会通过独立的 QUIC 流传输,因此供应商代码块中的数据包丢失不会影响仪表板代码块的加载。
从服务器推送到 103 Early Hints
HTTP/2试图通过服务器推送功能来解决服务器端的资源发现问题:服务器会主动发送浏览器尚未请求的资源。将相关信息提前传递的目标是正确的,但实际执行失败了。由于服务器无法可靠地判断浏览器是否已缓存该资源,因此常常会推送重复内容,浪费带宽,还会与浏览器认为更紧急的资源争夺资源占用空间。最终Chrome停止了对服务器推送功能的支持。
取代它的模型更合理地分配了职责:服务器负责提供信息,而浏览器则决定何时获取哪些资源。
103 Early Hints 将这一机制付诸实践。在服务器仍在生成完整响应时,它会发送一个包含 Link 头部的临时 103 状态码。浏览器可以立即开始获取这些资源,等到最终包含 HTML 的 200 OK 响应到达时,部分资源可能已经加载完成。
Browser Server
│ │
│──── GET / ─────────────→ │
│ │ (generating HTML...)
│ ←─── 103 Early Hints ─── │
│ Link: </assets/main.js>; rel=modulepreload
│ Link: </assets/vendor.js>; rel=modulepreload
│ │
│ (fetching chunks now...) │ (still generating...)
│ │
│ ←─── 200 OK + HTML ───── │
│ (chunks already downloading or done)
截至本文撰写时,NGINX 在 2025 年 6 月的 1.29.0 版本中已内置对 Early Hints 的支持,而 Cloudflare 则在其控制面板中提供相关开关功能。在 Node.js 中,响应对象提供了 writeEarlyHints() 方法,你可以在自定义服务器或中间件中,在发送实际响应之前调用它:
// In a custom server or middleware
res.writeEarlyHints({
link: [
"</assets/main.js>; rel=modulepreload; as=script",
"</assets/vendor.js>; rel=modulepreload; as=script",
"</assets/Dashboard.js>; rel=modulepreload; as=script",
],
});
// Then proceed with normal response
res.status(200).send(html);
该代码片段在最终响应时使用了类似 Express 的 res.status().send() 方法。而在纯 Node.js 的 http 服务器中,则应使用 res.writeHead() 和 res.end()。只有当服务器确实需要执行耗时操作,如数据库查询或页面渲染时,Early Hints 才能发挥作用,否则浏览器会处于空闲状态。
corewebvitals.io 基于 Chrome DevTools 的计时数据发现,将关键 CSS 文件通过 Early Hints 加载,可使 LCP 指标的加载时间比在 HTML 中常规预加载的方式提前约 35%。对于 React 应用而言,其优势在于能在服务器仍在运行时就开始下载主代码块,而非等到 HTML 已加载完成之后。
结合流式 SSR、Early Hints 与 HTTP/3
React的流式服务器渲染提供了另一种解决方案。服务器不会等到整个页面准备就绪才发送响应,而是分阶段发送HTML:
HTML shell → Suspense fallback → more HTML → resolved content → hydration
在渲染过程中,服务器能够知道哪些Suspense区域即将被渲染以及它们依赖哪些数据块。这些信息可以在HTML流开始之前通过Early Hints传递给浏览器。以下是简化的时间线:
0ms ── Browser sends request
── Server starts rendering
1ms ── Server knows Dashboard boundary will render
── Server sends 103 Early Hints: Dashboard.js
── Browser starts fetching Dashboard.js
50ms ── Server streams HTML shell
── Browser starts parsing
120ms── Server streams Dashboard content
── Dashboard.js already downloaded
── Hydration starts immediately
对比一下没有Early Hints的相同应用,此时发现过程需要等待客户端操作:
0ms ── Browser sends request
50ms ── Server streams HTML shell
── Browser starts parsing
── Browser discovers <script> tags
── main.js starts downloading
180ms── React executes
── Hits Dashboard lazy boundary
── Dashboard.js request starts (now)
300ms── Dashboard.js downloads
── Hydration starts
HTTP/3缩短了两个时间线中的每个数据段长度。Early Hints所改变的是Dashboard请求的启动时机,这能带来不同的、通常更大的节省效果。如果你的框架渲染功能已经由其他基础组件实现,那么React 19.2的SSR基础组件,如Activity和部分预渲染功能相关文章会进一步阐述流式传输边界的生成方式。
重新思考多路复用传输中的数据块粒度
长期以来,人们建议尽量减少HTTP请求次数,这是由于HTTP/1.1存在六连接限制,而HTTP/2的复用功能也仅能在单个TCP流上实现部分并行处理。有了独立的QUIC流后,请求数量不再像以前那样成为决定性因素,因此你可以更积极地拆分代码,而无需承担同样的每次请求惩罚。
一个合理的默认拆分方式如下:
- React和ReactDOM:单独的、稳定的供应商代码块。这类代码很少变动,因此可以拥有较长的缓存有效期。
- 路由库:出于同样的稳定性考虑,也需单独成块。
- 诸如图表库或编辑器之类的大型第三方库:每个库对应一个代码块,这样更新其中一个不会影响其他库。
React.lazy() 为每个路由生成一个代码块,这样导航时只会加载所需内容。这样做的优势在于缓存管理的精准性。修改 Dashboard.tsx 时只会使该路由的代码块失效,而第三方库的代码块仍可保留在缓存中,而这只有通过细粒度的拆分才能实现。在 HTTP/3 协议下,生成的 8 到 15 个代码块会通过独立的流进行加载,彼此之间不会因 HOL 现象而阻塞。
Vite 在无需配置的情况下即可处理大部分此类情况。在 webpack 中,splitChunks.cacheGroups 用于实现相同的策略。下面的配置会生成一个 React 供应商代码块、一个路由器代码块以及一个仅包含异步功能的图表代码块;priority 值用于决定当某个模块同时符合多个条件时优先属于哪一组,而 chunks: "async" 则可使图表相关代码不会被包含在初始加载中:
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
reactVendor: {
test: /[\\/]node_modules[\\/](react|react-dom|scheduler)[\\/]/,
name: "vendor-react",
chunks: "all",
priority: 40,
},
routerVendor: {
test: /[\\/]node_modules[\\/](react-router|react-router-dom)[\\/]/,
name: "vendor-router",
chunks: "all",
priority: 30,
},
chartsVendor: {
test: /[\\/]node_modules[\\/](recharts|d3)[\\/]/,
name: "vendor-charts",
chunks: "async",
priority: 20,
},
},
},
},
};
有一个需要注意的问题:过小的代码块会增加浏览器中每个文件的解析和编译开销。而且并非所有访问者都能使用HTTP/3——阻止443端口UDP流的企业防火墙很常见,这类用户只能使用HTTP/2,这样一来部分请求处理成本又会重新出现。在将代码拆分到比路由层级更细的级别之前,请先进行测试。通常按路由或按大型库来拆分是合适的粒度,而按组件拆分则往往不合适。如果你的技术栈是Next.js,Turbopack的代码分块控制功能相关内容介绍了Next.js中的类似设置。
为何预加载所有内容仍会适得其反
多路复用技术允许多个数据流共享同一连接,但它并不会为每个数据流提供无限的带宽,也不会让它们具有同等的重要性。20个modulepreload提示仍然共享同一条传输通道;竞争只是分散在更多的数据流之中。
因此,有意义的问题不是“什么可以预加载?”,而是“浏览器会在何时才意识到某个重要资源缺失?”作为起点,可考虑以下几点:
- 关键字体:使用
preload。 - 关键样式表:使用
preload;如果在页面渲染前就必须应用,则可使用preinit。 - 关键JavaScript模块:使用
preloadModule。 - 重要的跨域CDN或API源:使用
preconnect。
preload并设置fetchpriority="high"。preloadModule实现,由用户操作触发。prefetch。应将每条建议视为“可考虑选项”,而非强制规则。并不存在通用的预加载列表,只有那些既重要又在特定应用中较晚才被识别的资源需要预加载。
fetchpriority也同样受到限制。浏览器已经通过复杂的启发式方法对资源进行了优先级排序,将所有资源的优先级都设为high实际上等同于不设置任何优先级。只有当检测结果显示浏览器做出了错误判断时,才需要覆盖默认值。
审查加载策略的五个问题
在审计React应用程序如何加载资源时,按顺序思考以下问题:
- 该资源在何时被发现?如果真实答案是“JavaScript运行之后”或“React渲染之后”,那么很可能有机会让它更早被加载。
- 用户何时需要它?某样东西可能很重要,但并不一定需要立即使用。这一区别决定了是使用
preload(当前时刻)还是prefetch(空闲时间)。
preconnect,接着是preload或preloadModule,然后是preinit,再是103种早期提示,最后是基于服务器渲染内容生成的提示的流式SSR。各层之间的关联
从整体上看,加载现代 React 应用程序涉及六层结构,每一层都有其独立的职责范围:
Application intent (React knows which routes and components are needed)
↓
Resource APIs (preconnect / preload / preloadModule / preinit)
↓
Server-side surfacing (103 Early Hints / streaming SSR)
↓
Browser resource scheduler (priority, cache, bandwidth estimation)
↓
HTTP/3 / QUIC transport (independent streams, 0-RTT, connection migration)
↓
Network
旧的方法会尽可能将资源推送到客户端:使用服务器推送、预加载所有内容,以及合并代码包以减少请求次数。而当前的方法则是在每一层向浏览器提供更丰富的信息,由浏览器自行决定调度策略。HTTP/3 提供了一种能够高效处理这些决策的传输机制。提前提示功能能在 HTML 生成之前就将服务器的信息传递给浏览器。React 的资源 API 允许应用程序在组件渲染完成时明确表达其需求。
没有任何一种层可以替代另一种。HTTP/3无法获取那些尚未被发现的资源。当服务器不知道该使用哪条路由时,早期提示就毫无用处。而且,在中端手机上需要800毫秒才能编译的2 MB大型代码包,preloadModule也无法挽救这种情况。
关键要点
HTTP/3对预加载影响最大的并非传输速度的提升,而是更快的传输使得资源发现的成本相对上升。当400毫秒的传输时间缩短到80毫秒时,原本隐藏在其中的发现延迟便成了主要成本,此时针对旧有瓶颈制定的策略就不再适用了。
- 预加载资源是因为浏览器否则会来不及获取,而不仅仅是因为该资源很重要。那些能及时被发现的重要资源无需提示,而那些晚才被发现的不重要资源也不值得给予提示。
Suspense只能改善用户等待时的体验,无法让数据块更快加载。- 选择最有效的最低限度提示:对于资源地址使用
preconnect,对于文件使用preload,对于模块使用preloadModule,当需要应用或运行某些内容时则使用preinit。 - 根据悬停、聚焦等用户操作信号触发按路由分块的预加载,同时让早期提示和流式服务端渲染展示服务器已知晓的信息。
- 按路由及大型库进行拆分,牢记HTTP/2的回退方案,在进一步细化之前先进行性能测试。
HTTP/3 并没有让预加载变得过时,它只是更清晰地阐明了预加载的用途。
相关阅读
- React 19.2 SSR 基础知识:Activity、cacheSignal 与 PPR 解析 — 了解 React 19.2 中新增的 Activity 组件、cacheSignal 以及部分预渲染功能如何让开发者直接掌控服务器端渲染的性能。
- 通过 HTTP 驱动 Docling 流水线:从项目设置到索引化代码块 — 逐步讲解 Docling 流水线的 REST API:启动服务器、发现操作符、验证并运行数据摄取有向无环图,以及查看其执行监控数据。