Preact 11 到底改了什么:async 水合从 1 个 DOM 节点放开到 0 个或多个,preact/compat 单出口包最多瘦 28%
Preact 11 在 9 月 29 日正式发布,把开了六年的 Road to Preact 11(GitHub issue #2621)一次性收尾,10 月 6 日登上 JavaScript Weekly #805 头条。对准备升级的人来说,最实在的三个数字是:async 水合的节点限制从「必须 1 个」放开到「0 个或任意多个」、forwardRef 这层包装从用法里直接消失、preact/compat 做成可 tree-shake 之后代表性单出口包最多瘦身 28%。
六年攒出来的 major
Preact X(10.x)从 2019 年活到现在,稳定线一路走到 10.29.8(2026-08-01),之后 npm 上陆续出现 11.0.0-rc.0(2026-08-10)、rc.1、rc.2 三个预发布,9 月 29 日官方博客官宣 Preact 11,如今 latest 标签已经指向 11.0.1——发版几天就出了一个 patch,说明有人已经在生产环境里踩了。
官方对这次发布的定位很克制:不是重写,而是「把六年来攒下的 breaking changes 打包成一个 major」。官网的升级指南(preactjs.com/guide/v11/upgrade-guide)也写明:多数用户的改动是类型相关的,运行时行为变更集中在下面三处。
变化一:Hydration 2.0,异步边界不再卡在 1 个 DOM 节点
before(Preact 10):Suspense 边界在水合过程中暂停后恢复时,必须正好渲染 1 个 DOM 节点。返回 null、Fragment 或者两个并列元素,水合都会对不上,客户端只能整段重渲染,SSR 出来的 HTML 等于白发。
// Preact 10:AsyncProfile 恢复后必须是恰好 1 个 DOM 节点
<Suspense fallback={<Spinner />}>
<AsyncProfile /> {/* 返回 null 或 <><a/><b/></> → 水合 mismatch */}
</Suspense>
after(Preact 11):Hydration 2.0 允许挂起的组件返回 0 个或任意多个 DOM 节点,并且能和流式 SSR 的输出对上——同一个组件,恢复后返回 null、Fragment、多元素都可以安全水合。
// Preact 11:返回 null / Fragment / 多节点都能对上流式 SSR 的输出
<Suspense fallback={<Spinner />}>
<AsyncProfile /> {/* 用户没资料时返回 null,也不再触发整段客户端重渲染 */}
</Suspense>
变化二:refs 默认透传,forwardRef 整层壳被删掉
before:函数组件拿 ref 必须包一层 forwardRef,每个需要暴露内部 DOM 的组件都要多写一层样板。
// Preact 10
const Field = forwardRef((props, ref) => <input ref={ref} {...props} />);
after:ref 就是一个普通 prop,函数组件直接解构,forwardRef 不再需要(React 19 同款行为,从 React 迁移过来的人少记一条规则)。
// Preact 11
function Field({ ref, ...props }) {
return <input ref={ref} {...props} />;
}
变化三:hooks 依赖比较换成 Object.is,NaN 不再每次判定「变了」
before:hook 依赖用宽松相等比较,NaN == NaN 是 false,依赖数组里出现 NaN(比如解析失败的 Number(input))时,effect 每次渲染都重新执行。
after:Object.is(NaN, NaN) 为 true,useState 的初始值和 useEffect 的依赖数组里 NaN 都被稳定识别为「没变」,一次渲染只跑一次。
// Preact 11:input 解析失败时 deps=[NaN],不再无限重跑
useEffect(() => {
fetchProfile(Number(userId)); // userId 非数字 → NaN
}, [Number(userId)]);
升级时真正会炸的几处
官方说「多数改动是类型相关的」,但有几处是实打实的运行时/工程配置变更,升级前要过一遍:
- ESM-only:
preact@11.0.0的main字段直接指向dist/preact.mjs,而preact@10.29.8还是dist/preact.js。老构建链里用 CommonJSrequire('preact')的地方会直接失败,webpack 4 / 老 Node 脚本要先确认能解析.mjs。 - TypeScript 5.1+ 强制:JSX 类型从全局
JSXnamespace 挪进preactnamespace,useRef要求必填初始值(对齐 React 19);类型报错集中在这两项,全局搜一下JSX.和无参useRef()就能清干净。 - IE11 正式出局,浏览器目标整体更新。
- 挪家的 API:
defaultProps和「自动补 px」都搬进了preact/compat;component.base、SuspenseList、replaceNode被彻底删除,用到就是编译期报错。 - 生态基本就绪:官方的
@preact/signals、preact-render-to-string、preact-iso、prefresh、@preact/preset-vite从预发布阶段就支持 11,npm ls preact看到的多数一方依赖不用等更新。
下一步怎么做
先别动业务代码,三条命令把升级面摸清:
npm view preact@latest version # 当前 11.0.1
npx tsc --version # 必须 ≥ 5.1,否则先升 TS
npm ls preact # 看清哪些依赖还钉在 10.x
然后打开 preactjs.com/guide/v11/upgrade-guide,按 Hydration 2.0、refs 默认透传、Object.is 三个小节逐项核对自己项目的用法——这三节正好对应上面三个运行时变化,核对完再执行 npm install preact@11,一次编译通过的概率比直接装高得多。
评论区
登录后可评论。