ATRULE 技术博客ATRULE 技术博客
首页
博客
文档
关于
首页
博客
文档
关于
  • 技术文章

    • VuePress 2.x 完全指南
    • Gemini3 & GPT5 & DeepSeek3:AI时代程序员的身份转变
    • Agent 架构选型指南:单智能体 vs 多智能体
    • Agent 工程师转型学习路线图 (Full-Stack to Agent Engineer)
    • Agent 工程师学习笔记
    • 📖 Agent 系统架构与工程实践面试速记卡片
    • Agent 流式对话前端踩坑记录
    • RAG 检索工程问题记录
    • Agent 后端问题记录(Koa / MongoDB)
    • LLM 调用与 Agent 编排问题记录
    • Electron 桌面端问题排查记录
    • AI Agent 全栈开发问题排查记录
    • ES2026 与 TypeScript 进阶问题记录
    • 浏览器渲染与 V8 内存问题记录
    • React 19 与 Vue 4 新内核问题记录
    • 前端工程化与微前端问题记录
    • 性能、安全与可观测性问题记录
    • 前端架构实战问题记录
  • 项目实战

    • Node.js + Koa 抖音直播弹幕Agent 五大核心模块(面试项目完整版,TS技术栈)★★★★★
    • Poiclaw 项目蓝图:自主编程实体
    • AI旅行助手

React 19 与 Vue 4 新内核问题记录

记录 React 19 Compiler/RSC 与 Vue4 Signal/Vapor 落地时踩过的坑,每个问题都是实际遇到过并解决掉的。

问题一:开 React Compiler 后出现"缓存命中副作用消失"bug

现象:升级 React Compiler(Forget)后,某个组件的埋点偶发不触发。

原因:组件在渲染期写了 userCache.current.set(id, data)(useRef 的 Map)。Compiler 缓存的是表达式级结果——缓存命中时这段代码不执行,写副作用消失。这是代码 bug,不是编译器 bug。

解决:副作用移出渲染期(effect/事件回调);切换前先跑 eslint-plugin-react-hooks 的 compiler 诊断模式,按报错清单清理渲染期副作用和外部可变依赖。手写 useMemo/useCallback 残留场景:ref 可变对象参与渲染、每次语义上要新的对象。

问题二:'use client' 不是"这个文件在客户端跑"

现象:以为 'use client' 只标记当前文件,结果客户端 bundle 爆炸。

原因:它标记的是客户端边界入口,这个文件的所有 imports 会一起进客户端 bundle;且客户端组件不能 import 服务端组件,只能以 children 形式组合。

解决:把 'use client' 推到树的最深处(交互叶子),数据获取/重计算留服务端。props 必须可序列化(函数除 Server Actions 外传不过去)。

问题三:点赞失败后 UI 没回滚

现象:手写的"先 set 再请求"乐观更新,请求失败后点赞数停在错误值。

原因:自己维护乐观态,忘了写 undo;且没有 transition 语义,回滚时机不可靠。

解决:用 useOptimistic——它是指向 base state 的派生视图层,transition 结束自动丢弃乐观层(失败即回滚):

const [likes, addOptimistic] = useOptimistic(baseLikes, (cur, delta: number) => cur + delta);
function like() {
  startTransition(async () => {
    addOptimistic(1);
    await likeAction(); // throw → transition 结束,乐观层自动消失
  });
}

连续点 N 次 = N 层乐观叠加在同一 base 上;服务端以最终 base 为准。

问题四:Vue4 细粒度更新后为什么不需要 vdom diff

现象:想给团队讲清 Vue4 Signal 和 Vue3 的本质区别,发现背 Vue3 源码没用。

原因:Vue3 粒度是组件级——信号变了整个组件重渲染靠 diff;Vue4 Signal 的订阅者直接是"更新某个文本节点"的副作用函数,更新 = 执行几个闭包,不建 vnode 树。Vapor 模式在编译期把依赖声明进产物(setText(el0, () => count.value))。

解决:理解 Signal 三件套就能手写——订阅(get 收集)、触发(set 通知)、微任务批处理;computed 用 dirty 标记 + 值相等检查防止无效传播(min(A,B) 里改的是较大者时下游不重跑)。

问题五:搜索联想快速输入时闪旧值

现象:useDeferredValue 包了联想结果,快速输入时旧结果先渲染出来一瞬。

原因:deferred value 的语义就是"旧值立刻渲染,新值低优渲染"——闪旧值是特性不是 bug;且 transition/deferred 只管渲染优先级,不取消请求。

解决:请求侧只认最新一次(AbortController 取消旧的),渲染侧接受旧值或用占位骨架;输入框受控 state 保持高优(不包 transition),只有联想结果消费低优。选型口诀:useTransition 包"更新的发起",useDeferredValue 包"值的消费"(适合外部传 props 场景)。

Prev
浏览器渲染与 V8 内存问题记录
Next
前端工程化与微前端问题记录