第 32 章:Document Metadata 与资源预加载——title、style、preload
React 19 之前,改页面 <title> 得自己操作 document.title,引样式要手动塞 <link>,烦且容易乱。19 带来了两样东西:元数据提升(在组件里直接写 <title>/<meta>/<link>,React 帮你搬到 <head>)和资源预加载(preload 系列提前拉关键资源)。
function ProfilePage() {
return (
<>
<title>我的主页</title>
<meta name="description" content="个人简介" />
<link rel="stylesheet" href="/profile.css" precedence="high" />
<ProfileContent />
</>
);
}
先记住这一句:元数据提升 = 你在任意组件里写 title/meta/link,React 把它提升到
<head>,同名去重、按precedence排顺序;预加载 = 用preload/preinit系列提前把关键资源拉起来,减少首屏等待。
一图流:元数据与预加载两条线
元数据提升:写在组件里,住在 head 里
以前 <title> 只能出现在 <head>,React 19 允许你在任意组件里写它。运行时(Float 系统,ReactDOMFloat)会把它识别为"可提升资源",从渲染位置搬到 <head>,并保证:
- 去重:两个组件都写了
<title>,head 里只有一个;同名<link>同理。 - 优先级:多个冲突资源(比如两个同名样式表),按
precedence决定哪个生效、按什么顺序插入。 - 服务端一致:SSR 时提升逻辑照跑,客户端水合后保持一致,不会闪 title。
样式表:从"手动管理"到"声明式"
<link rel="stylesheet" precedence="high"> 是 19 的关键新用法。以前你手动把 <link> 塞进 head、自己保证顺序;19 里你只需声明"这个样式表优先级多高",React 负责:
- 把它提升进 head。
- 按
precedence排序(相同优先级的按首次渲染顺序)。 - 样式表就绪前,用它包裹的内容先等它(避免 FOUC,样式闪变)。
这就是 precedence 的用途:让"后渲染的组件里引的样式"不会把"先渲染的样式"冲掉。
资源预加载:preload 系列
预加载 API 是一组"提前动手"的函数(都在 ReactDOMFloat,ReactDOMFloat.js:89):
| API | 干什么 | 源码 |
|---|---|---|
| preconnect(58) | 提前建连接(DNS + TCP),给跨域资源省握手时间 | ReactDOMFloat.js:58 |
| preload(89) | 提前拉资源进缓存,用时秒出(图片、字体、脚本) | ReactDOMFloat.js:89 |
| preloadModule(153) | 提前拉 ES module | ReactDOMFloat.js:153 |
| preinit(205) | 提前执行(样式表、脚本),不等用就直接跑 | ReactDOMFloat.js:205 |
它们是给浏览器"提前热身"用的:你知道这个资源马上要用,但还没渲染到它,先 preload 拉起来,真正用时网络层已经就绪。对首屏性能,这是"把等待提前"的关键手段。
为什么值得单独讲
这两块在 19 之前的 React 里都不是一等公民:元数据要靠副作用手动改 document.head,资源要靠手写 <link> 或第三方库。19 把它们声明式化:告诉 React"这里有个 title、这个样式表优先级高、这个资源提前拉",剩下的提升、去重、排序、预加载,React 全包。这让页面级的东西第一次能"跟着组件走"。
动手实验
- 看提升:在组件深处写
<title>,渲染后查document.head,确认 title 被提升到 head 且只有一个。 - 看 precedence:两个
precedence不同的<link>样式表,查 head 里的插入顺序是否按优先级。 - 看 preload:在
preload(ReactDOMFloat.js:89)打断点,看它怎么给浏览器发"提前拉"的信号,Network 面板确认资源提前加载。
小结
- 元数据提升:任意组件里写 title/meta/link → 自动搬到 head,去重、按
precedence排顺序。 - 样式表声明式管理:
precedence排序 + 就绪前防止样式闪变。 - 预加载系列:
preconnect(58) /preload(89) /preloadModule(153) /preinit(205),提前热身。 - 19 把"页面级的东西"从副作用变成声明式,跟着组件走。
- 下一步:19 到底删了什么、为什么删,第 33 章。