
一句话:
useInsertionEffect是为 CSS-in-JS 库设计的第三个 Effect——它在「DOM 变更之前」同步执行,让「运行时生成的样式」赶在「浏览器计算布局」之前进入样式表,从而避免「额外一次重排」与「样式闪烁(FOUC)」。
一、三个 Effect 的完整时序
一次更新的完整时间轴(含三个 Effect):
① render 阶段(可中断)
· 执行组件函数、diff、收集 flags
② commit 阶段
a. before mutation(读取 DOM 快照,如 getSnapshotBeforeUpdate)
b. ⭐ useInsertionEffect 的清理 + 执行 ← 【DOM 变更之前】
c. mutation(把 DOM 变更应用到真实 DOM)
· 执行 useLayoutEffect 的【清理函数】
· 卸载的节点上把 ref 置为 null
d. layout
· 赋值新 ref
· ⭐ useLayoutEffect 的清理 + 执行 ← 【DOM 已更新、绘制前】
e. (commit 结束)
③ 浏览器绘制(paint) ← 用户在这一刻看到界面
④ passive effects(异步)
· ⭐ useEffect 的清理 + 执行 ← 【绘制之后】
| Effect | 执行时机 | 能读 DOM / ref | 能做副作用 | 会阻塞绘制 | 设计用途 |
|---|---|---|---|---|---|
useInsertionEffect |
DOM 变更之前 | ❌(DOM 还是旧的、ref 未赋值) | ⚠️ 只允许「插入样式」 | ✅ | CSS-in-JS 插入 <style> / insertRule |
useLayoutEffect |
DOM 变更后、绘制前 | ✅ | ✅(同步) | ✅ | 测量布局、避免闪烁 |
useEffect |
绘制之后(异步) | ✅ | ✅ | ❌ | 请求、订阅、日志 |
⭐ 一句话记住顺序:
useInsertionEffect(DOM 变更前)→ DOM 变更 → useLayoutEffect(绘制前)→ 绘制 → useEffect(绘制后)
二、为什么需要它:CSS-in-JS 的两个难题
难题一:用 useLayoutEffect 插样式 → 多一次重排
// ❌ 用 useLayoutEffect 插入运行时样式
function BadButton() {
useLayoutEffect(() => {
injectStyle('.btn { color: red; }'); // ⚠️ 此时 DOM 已经变更
});
return <button className="btn">确定</button>;
}
时序问题:
① DOM 变更(<button class="btn"> 已进入 DOM)
② 浏览器「计算样式 + 布局」——但此时 .btn 规则【还不存在】
→ 浏览器按「没有样式」算一遍布局
③ useLayoutEffect 执行 → 插入 .btn 规则
④ 样式表变了 → ⭐ 浏览器【必须重新计算样式 + 重新布局】
→ 一次「额外的强制同步布局(forced reflow)」
⭐ 后果:
· 每个「首次渲染的样式化组件」都会触发一次额外重排
· 一个页面几十个组件 = 几十次额外重排 → 首屏明显变慢
· 在「渲染中测量尺寸」的组件里还会读到「错误的中途布局」
难题二:用 useEffect 插样式 → 样式闪烁
// ❌ 用 useEffect 插入
function BadCard() {
useEffect(() => {
injectStyle('.card { padding: 16px }'); // ⚠️ 绘制之后才插入
});
return <div className="card">内容</div>;
}
时序问题:
① DOM 变更 → ② 浏览器绘制(此时 .card 规则不存在)→ 用户看到「无样式的卡片」
③ useEffect 执行 → 插入样式 → ④ 浏览器重绘 → 用户看到「有样式的卡片」
⭐ 后果:用户看到「一闪而过的无样式内容」(FOUC)
—— 这在「首屏」和「服务端渲染后的水合」阶段尤其明显。
⭐⭐ 两个难题的本质:
样式必须【在浏览器计算布局之前】就位。
· useLayoutEffect 太晚(布局已经算过一轮)
· useEffect 更晚(绘制都发生了)
→ React 需要「在 DOM 变更之前」也提供一个 sync effect 位置。
→ 这就是 useInsertionEffect(React 18 引入)。
三、官方对它的定位与约束
⭐⭐ 关键定位(React 官方文档原话的意思):
「useInsertionEffect 是为 CSS-in-JS 库作者设计的。
如果你不是在构建 CSS-in-JS 库,你应该用 useEffect 或 useLayoutEffect。」
⭐ 三条硬性约束:
① 【不能读 ref】
DOM 还没变更 → ref 里还是「旧的元素」或 null
→ 读了也没有意义(而且规范明确不允许)
② 【不能 setState】
在这个阶段触发更新会打乱 commit 流程
→ 你会收到警告("Cannot update state from useInsertionEffect")
③ 【不能依赖它做任何「有观察意义」的事】
它只用来「往文档里插一段静态样式」——
不要去读写 DOM、不要做埋点、不要发起请求
// ✅ 唯一正确的用法:插入样式(无副作用、无状态、无 DOM 读写)
useInsertionEffect(() => {
const rule = sheet.insert(`.${key} { color: ${color} }`);
return () => sheet.remove(rule);
}, [key, color]);
四、完整实现:一个可运行的 mini CSS-in-JS(180 行)
// mini-css.ts —— 用 useInsertionEffect 实现一个极简 CSS-in-JS 运行时
import { useInsertionEffect, useMemo } from 'react';
/** ============ 1. 规则去重:同样的 CSS 只插一次 ============ */
function hash(input: string): string {
// djb2:快速、非加密、足够避免碰撞(生产库常用同类方案)
let h = 5381;
for (let i = 0; i < input.length; i++) {
h = ((h << 5) + h + input.charCodeAt(i)) | 0;
}
return (h >>> 0).toString(36);
}
/** ============ 2. 容器:按「层(tag)」分组,保证插入顺序稳定 ============ */
interface Container {
el: HTMLStyleElement;
sheet: CSSStyleSheet;
/** ruleKey → 在 sheet.cssRules 里的下标(用于删除) */
index: Map<string, number>;
/** ruleKey → 引用计数(多个组件共用同一条规则时不能提前删) */
refCount: Map<string, number>;
}
const containers = new Map<string, Container>();
/** 服务端渲染时收集规则(服务端没有 DOM,走「渲染期收集」而不是 effect) */
const serverRules = new Map<string, Map<string, string>>();
function isServer() {
return typeof document === 'undefined';
}
function getContainer(tag: string): Container {
let c = containers.get(tag);
if (!c) {
const el = document.createElement('style');
el.setAttribute('data-mini-css', tag);
document.head.appendChild(el);
c = { el, sheet: el.sheet!, index: new Map(), refCount: new Map() };
containers.set(tag, c);
}
return c;
}
/**
* ⭐ 插入规则:用 sheet.insertRule() 而不是 el.textContent += css
* 原因:改 textContent 会让浏览器「重新解析整个样式表」;
* insertRule 是增量操作,代价小得多(这是所有 CSS-in-JS 库的共识)
*/
function insertRule(container: Container, ruleKey: string, css: string): void {
const count = container.refCount.get(ruleKey) ?? 0;
if (count === 0) {
const rule = `.${ruleKey}{${css}}`;
try {
const at = container.sheet.cssRules.length; // 追加到末尾,保证顺序
container.sheet.insertRule(rule, at);
container.index.set(ruleKey, at);
} catch (err) {
// 规则非法时不要让整个应用崩掉
console.warn('[mini-css] 插入规则失败:', css, err);
}
}
container.refCount.set(ruleKey, count + 1);
}
/** 移除规则:引用计数归零才真正删除 */
function removeRule(container: Container, ruleKey: string): void {
const count = (container.refCount.get(ruleKey) ?? 1) - 1;
if (count > 0) {
container.refCount.set(ruleKey, count);
return;
}
const at = container.index.get(ruleKey);
if (at !== undefined) {
try {
container.sheet.deleteRule(at);
} catch {
/* 已被移除则忽略 */
}
container.index.delete(ruleKey);
// ⚠️ 删除后,后面所有规则的下标都要前移 —— 这里做一次简单重算
container.index.forEach((value, key) => {
if (value > at) container.index.set(key, value - 1);
});
}
container.refCount.delete(ruleKey);
}
/** ============ 3. ⭐ 核心 Hook:在「DOM 变更之前」把样式插进去 ============ */
export function useStyle(css: string, tag = 'default'): string {
const ruleKey = useMemo(() => `c${hash(css)}`, [css]);
// 服务端:在「渲染期」收集(effect 在服务端不执行)
if (isServer()) {
const bucket = serverRules.get(tag) ?? new Map<string, string>();
if (!bucket.has(ruleKey)) bucket.set(ruleKey, css);
serverRules.set(tag, bucket);
return ruleKey;
}
// 客户端:⭐ 用 useInsertionEffect(不是 useLayoutEffect / useEffect)
useInsertionEffect(() => {
const container = getContainer(tag);
insertRule(container, ruleKey, css);
return () => removeRule(container, ruleKey);
}, [ruleKey, css, tag]);
return ruleKey;
}
/** ============ 4. SSR:把收集到的规则吐成 <style> 字符串 ============ */
export function extractStyles(): string {
const parts: string[] = [];
serverRules.forEach((bucket, tag) => {
const rules: string[] = [];
bucket.forEach((css, key) => rules.push(`.${key}{${css}}`));
parts.push(`<style data-mini-css="${tag}">${rules.join('')}</style>`);
});
serverRules.clear();
return parts.join('');
}
/** ============ 5. 一个组件级 API:类似 styled 的极简版 ============ */
export function useClassNames(rules: Record<string, string>, tag = 'default'): Record<string, string> {
const keys: Record<string, string> = {};
// ⚠️ 注意:这里不能用 map 里调 Hook(Hook 规则),所以要「展开成固定调用」
// 真实库会把「一次渲染的规则」收集起来,用「一个 useStyle(聚合后的 css)」处理
Object.keys(rules).forEach((name) => {
keys[name] = `pending-${name}`;
});
return keys;
}
⭐⭐ 这个实现里最值得注意的四个设计(都是「库级细节」):
① 用 `sheet.insertRule()` 而不是改 `textContent`
→ 前者是「增量插入」,后者会「重新解析整张样式表」
② 引用计数(refCount)
→ 同一个 CSS 规则被 100 个组件用到时,只能插一次、
且要等「最后一个使用者卸载」才能删
③ 按「层(tag)」分多个 `<style>`
→ 保证「同类规则的相对顺序稳定」,
避免「后插入的规则被前面的覆盖」(specificity 顺序问题)
④ 服务端走「渲染期收集」而不是 effect
→ 服务端没有 DOM,也没有 effect;
客户端靠 useInsertionEffect 插入,服务端靠 extractStyles() 注入 HTML
// ✅ 使用示例:一个带动态颜色的按钮
import { useStyle } from './mini-css';
function Button({ color, children }: { color: string; children: React.ReactNode }) {
// ⭐ 动态样式:颜色不同 → css 不同 → ruleKey 不同(各插一条规则,互不影响)
const cls = useStyle(`
display: inline-flex;
align-items: center;
padding: 8px 16px;
border-radius: 6px;
background: ${color};
color: #fff;
transition: filter .15s;
`);
return <button className={cls}>{children}</button>;
}
function App() {
return (
<>
<Button color="#2563eb">主按钮</Button>
<Button color="#dc2626">危险按钮</Button>
{/* ⭐ 浏览器只会“算一次布局”:样式在 DOM 变更前就已就位 */}
</>
);
}
// ✅ SSR 集成(Next.js App Router 示意)
import { extractStyles } from './mini-css';
export default function RootLayout({ children }: { children: React.ReactNode }) {
const styleTags = extractStyles(); // ⚠️ 必须在「整个树渲染完之后」调用
return (
<html>
<head>
{/* ⭐ 把服务端收集的规则注入 HTML,避免水合前的无样式闪烁 */}
<style dangerouslySetInnerHTML={{ __html: styleTags.replace(/<\/?style[^>]*>/g, '') }} />
</head>
<body>{children}</body>
</html>
);
}
五、真实库是怎么做的
⭐ 主流 CSS-in-JS 库都依赖 useInsertionEffect(React 18+):
· Emotion:内部用 useInsertionEffect 把序列化后的样式插进它的 StyleSheet
· styled-components v6:改用 useInsertionEffect(v5 用 useLayoutEffect,有额外重排问题)
· styled-jsx(Next.js 内置):同样依赖 insertion 时机
· 零运行时方案(vanilla-extract / Linaria / Panda)不需要它
→ 因为样式在「构建时」就被抽成静态 CSS 文件了
⭐⭐ 为什么会「从 useLayoutEffect 迁移到 useInsertionEffect」?
因为 useLayoutEffect 有两个问题(见第二节):
① 「额外一次强制同步布局」——每个首次渲染的样式化组件都触发
② 在「并发渲染」下更麻烦:渲染可能被打断,
useLayoutEffect 的执行与「样式插入」的时机可能错位
✅ useInsertionEffect 的价值:
· 「DOM 变更之前」就把样式放进样式表
→ 浏览器只算一次布局(没有「先无样式、后有样式」的两轮)
· 时机更早、更确定,与并发渲染的配合更好
六、与 React 19「样式表提升」的关系
// React 19 允许「把 <link> / <style> 直接写在组件里」,React 会处理去重与顺序
function Article() {
return (
<article>
<link rel="stylesheet" href="/article.css" precedence="default" />
<style href="article-inline" precedence="high" dangerouslySetInnerHTML={{ __html: '.x{color:red}' }} />
<p className="x">内容</p>
</article>
);
}
⭐ 两者的分工:
· useInsertionEffect:**库作者**用来「运行时插入样式」(动态、不可静态提取)
· React 19 的样式表提升:**应用作者**用来「声明式引入样式表」,
由 React 负责「去重、按 precedence 排序、在合适时机插入」
→ 适用于「静态 CSS / 第三方样式表」
⭐ 结论:
如果你的样式能「编译时提取」→ 根本不需要 useInsertionEffect(用 vanilla-extract / Tailwind)
如果必须「运行时生成」→ 用 useInsertionEffect(或直接用成熟库)
| 方案 | 样式生成时机 | 需要 useInsertionEffect | 运行时开销 |
|---|---|---|---|
| CSS Modules / Tailwind | 构建时 | ❌ | 无 |
| vanilla-extract / Linaria / Panda | 构建时(写法像 JS) | ❌ | 无 |
| Emotion / styled-components | 运行时 | ✅ | 中(序列化 + 插入) |
React 19 样式表提升(<link precedence>) |
构建/网络 | ❌(React 内部处理) | 无 |
七、什么时候不该用它
⭐ 四条「不该用」的判断:
① 你不是在写 CSS-in-JS 库 → 不要用
(官方明确说过:应用代码应该用 useEffect / useLayoutEffect)
② 样式可以「构建时提取」→ 不需要它
(CSS Modules / Tailwind / vanilla-extract 都能静态产出 CSS)
③ 你只是想「动态改几个样式」→ 不需要它
✅ 用 CSS 变量 + 内联 style:
<div style={{ '--btn-bg': color } as React.CSSProperties} className="btn" />
CSS 里:.btn { background: var(--btn-bg); }
→ 零运行时、零额外重排
④ 你只是想让「首屏不闪」→ 先在构建时把样式抽出来
(服务端渲染时「渲染期收集 + 注入 HTML」是更根本的解法)
// ✅ ③ 的推荐做法:CSS 变量代替「运行时生成 CSS」
// styles.css(构建时产出,零运行时)
// .progress { width: var(--progress, 0%); transition: width .2s }
// .avatar { background: var(--avatar-bg, #eee) }
function Progress({ value }: { value: number }) {
return (
<div
className="progress"
style={{ '--progress': `${value}%` } as React.CSSProperties} // ⭐ 只传变量
/>
);
}
八、本篇特有的坑
// ① 在 useInsertionEffect 里读 ref
useInsertionEffect(() => {
const h = ref.current.offsetHeight; // ❌ DOM 还没变更,读到的是旧值/null
}, []);
// ② 在 useInsertionEffect 里 setState
useInsertionEffect(() => {
setReady(true); // ❌ 会打乱 commit 流程(React 会警告)
}, []);
// ③ 用 useLayoutEffect 代替它来插样式 → 每个组件一次额外重排
useLayoutEffect(() => { inject(css); }, []); // ⚠️ 布局已经算过一遍了
// ④ 用 useEffect 代替它 → 样式闪烁(FOUC)
useEffect(() => { inject(css); }, []); // ⚠️ 绘制之后才插
// ⑤ 用 textContent 追加样式而不是 insertRule → 每次重新解析整张表
styleEl.textContent += `.${key}{${css}}`; // ❌ O(n) 解析
container.sheet.insertRule(`.${key}{${css}}`, container.sheet.cssRules.length); // ✅
// ⑥ 忘记「引用计数」→ 一个组件卸载就把「别人还在用的规则」删了
// 现象:组件 A 卸载后,组件 B 的样式突然消失
// ⑦ 删除规则后「不修正后续下标」→ 下次删除删错规则
// (CSSOM 删除规则会让后面的规则下标前移)
// ⑧ 忘记「按层分组」→ specificity 顺序不稳定
// 现象:同样的两条规则,插入顺序变化导致「样式时好时坏」
// ⑨ SSR 时「在 effect 里收集样式」→ 服务端不执行 effect,什么也收集不到
// ✅ 服务端必须在「渲染期」收集(把收集逻辑放在 useStyle 的函数体里)
// ⑩ 调用 extractStyles() 的时机太早 → 收集不到「后面才渲染的组件」
// ✅ 必须在「整棵树渲染完之后」(如 layout 组件的末尾 / renderToString 之后)
// ⑪ 把「动态值」直接拼进 CSS 而不做转义 → CSS 注入
useStyle(`color: ${userProvidedColor}`); // ⚠️ 若 color 里含 `;}` 可注入任意 CSS
// ✅ 校验/白名单(如只允许颜色字面量或 CSS 变量名)
// ⑫ 用 useInsertionEffect 做「非样式」的事(埋点、请求、日志)
// ⚠️ 时机语义不对(它在 DOM 变更前),而且会被「并发渲染下的重放」影响
// ⑬ 在「并发渲染」下假设「insertion effect 不会被重复执行」
// ⚠️ 与其它 effect 一样,它也可能被「挂载→卸载→再挂载」(StrictMode)
// → 所以清理函数必须能把「插入的规则」正确撤销
// ⑭ 忘记「服务端的 style 顺序」要稳定
// 服务端收集顺序与客户端插入顺序不一致 → 水合后样式可能变化(轻微 FOUC)
// ⑮ 在「微前端」里多个应用各自用一套 CSS-in-JS → 样式表数量爆炸
// ✅ 共享一个运行时(或统一换成构建时方案)
// ⑪ 的完整防护写法
function sanitizeColor(input: string): string {
// 只允许 #hex / rgb() / hsl() / 命名色 / CSS 变量
if (/^#[0-9a-f]{3,8}$/i.test(input)) return input;
if (/^(rgb|hsl)a?\([\d\s.,%/]+\)$/i.test(input)) return input;
if (/^var\(--[\w-]+\)$/.test(input)) return input;
if (/^[a-z]+$/i.test(input)) return input; // 命名色
return 'currentColor'; // 兜底
}
const cls = useStyle(`color: ${sanitizeColor(color)}`);
九、面试延伸
- 「
useInsertionEffect是做什么的?」
它是 React 18 引入的第三个 Effect,专为 CSS-in-JS 库设计——在「任何 DOM 变更之前」同步执行。它解决的问题是:运行时生成的样式必须「在浏览器计算布局之前」进入样式表,否则会付出额外代价(用useLayoutEffect→ 布局已算过一轮,插入样式后要重新计算样式 + 重新布局;用useEffect→ 绘制之后才插入,用户会看到无样式内容一闪)。 -
「三个 Effect 的执行顺序是什么?」
useInsertionEffect(DOM 变更前)→ DOM 变更 →useLayoutEffect(DOM 已更新、绘制前)→ 浏览器绘制 →useEffect(绘制之后,异步)。记忆方法是「越早的越不能碰 DOM」:useInsertionEffect时 DOM 还没变(不能读 ref),useLayoutEffect时 DOM 已更新(能测量),useEffect时已经绘制(不会阻塞)。 -
「为什么
useInsertionEffect里不能读 ref?」
因为它在「DOM 变更之前」执行——此时真实的 DOM 还是旧的,ref也还没被赋值(ref 在 commit 的 mutation/layout 阶段才更新)。所以读 ref 得到的是「旧元素或 null」,读了没有意义。官方也明确限制:「不要在useInsertionEffect里读 ref、更新 state,或做任何有副作用的事」——它只该用来「往样式表里插规则」。 -
「为什么 CSS-in-JS 库不用
useLayoutEffect?」
因为会产生「额外一次强制同步布局」:useLayoutEffect在「DOM 变更之后」执行,此时浏览器已经算过一轮布局(按「没有这些样式」算的);样式插入后,浏览器必须重新计算样式 + 重新布局。一个页面有几十个「首次渲染的样式化组件」就会有几十次额外重排,首屏明显变慢。useInsertionEffect把插入时机提前到「DOM 变更之前」,浏览器只算一次布局。 -
「为什么不用
useEffect?」
因为useEffect在「绘制之后」执行——用户会先看到「无样式的 DOM」,然后样式插入、重绘,出现一闪而过的无样式内容(FOUC)。这在「首屏」与「SSR 水合」阶段尤其明显。所以「插样式」既不能用useEffect(太晚,会闪),也不能用useLayoutEffect(会多一次重排),只有useInsertionEffect时机正确。 -
「应用代码需要用
useInsertionEffect吗?」
不需要(除非你在写 CSS-in-JS 库)。官方的定位很明确:「它是为库作者设计的;如果你是构建应用,应该用useEffect或useLayoutEffect」。应用里遇到「动态样式」应该优先用 CSS 变量 + 内联 style(零运行时、零额外重排),或者用「构建时可提取」的方案(CSS Modules / Tailwind / vanilla-extract)。 -
「实现一个 CSS-in-JS 运行时要注意什么?」
六个关键点:① 用sheet.insertRule()而不是改textContent(后者会重新解析整张样式表);② 引用计数——同一条规则被多个组件使用时,只能插一次、等最后一个使用者卸载才能删;③ 按「层」分组多个<style>——保证同类规则的相对顺序稳定(避免 specificity 顺序问题);④ 删除规则后要修正后续规则的下标(CSSOM 删除会让后面的下标前移);⑤ SSR 走「渲染期收集」(服务端没有 DOM 与 effect),用extractStyles()注入 HTML;⑥ 动态值要转义/白名单(防止 CSS 注入)。 -
「
useInsertionEffect和 React 19 的『样式表提升』有什么关系?」
两者解决不同层的问题:①useInsertionEffect面向「库作者」——用于「运行时生成、无法静态提取」的样式(如 Emotion / styled-components);② React 19 的<link precedence>/<style>面向「应用作者」——声明式引入「静态样式表」,由 React 负责去重、排序(按 precedence)、在合适时机插入。如果你的样式能在构建时提取,根本不需要useInsertionEffect(用 CSS Modules / Tailwind / vanilla-extract)。 -
「为什么这个 Hook 会是『库专用』的?」
因为它暴露的是「渲染管线的一个内部时机」——普通应用代码不需要知道「DOM 变更之前」这个窗口,用它反而容易写出「读 ref、setState、做埋点」之类的错误用法。而 CSS-in-JS 库必须在这个时机插入样式,所以 React 专门为它们开了一个口子。这类「为库设计的 API」在 React 里还有useSyncExternalStore(为状态库)、useImperativeHandle(为封装组件)。 -
「如果我不小心在
useInsertionEffect里读了 DOM 会怎样?」
不会报错,但会得到错误的结果——读到的是「变更前的 DOM」(旧的尺寸/内容)或null。如果你用它做「测量后设置样式」,就会出现「用上一次的尺寸渲染这一次」(与闭包陷阱症状类似)。而如果在那里setState,React 会发出警告(因为它会打乱 commit 的流程)。结论:它不是「更早的useLayoutEffect」,而是「样式表专用窗口」——不能拿它当useLayoutEffect用。
一句话速记
useInsertionEffect在「DOM 变更之前」同步执行,专供 CSS-in-JS 库插样式——顺序是「useInsertionEffect(DOM 变更前)→ DOM 变更 →useLayoutEffect(绘制前)→ 绘制 →useEffect(绘制后)」;用useLayoutEffect会多一次强制同步布局、用useEffect会闪无样式内容(FOUC),所以只有它时机正确;三条禁令:「不能读 ref(DOM 还是旧的)、不能 setState、不能做任何有观察意义的事」;实现要点是「sheet.insertRule()而不是改textContent、引用计数、按层分多个<style>保证顺序、删除后修正下标、SSR 在渲染期收集 +extractStyles()注入、动态值白名单防注入」;应用代码不要用它——优先「CSS 变量 + 内联 style」或「构建时可提取的方案」(CSS Modules / Tailwind / vanilla-extract),React 19 的<link precedence>也由 React 自己处理这类事。



最新评论
读过书不知道欧·亨利的人少。教科书上选文有
这小生活不错呀
不错,必须顶一下!
看着你还在坚持,很好
看来忙了也没时间更新博客了
NIce。学习了。。。。
网站不错!!!!
简洁实用,好文章!