{"ok":true,"data":{"slug":"vue2-to-react19","title":"从 Vue 2 到 React 19 思维方式的三个转弯","summary":"响应式、模板编译、组件通信——这三个概念在 React 里都有对应的替代品，但真正需要换掉的是写代码时脑子里的顺序。","date":"2026-09-28","category":"技术笔记","tags":["React","Vue","思维"],"cover":"/images/covers/cover-rebuild.svg","featured":false,"draft":false,"body":"\n写 Vue 2 的时候，我几乎不需要想「数据变了界面会怎么变」。`this.xxx = 1` 之后，所有用到它的地方自动更新。这种「不用管」的舒适，是我这次重构最大的适应成本。\n\n## 第一个转弯 从「双向绑定」到「单向数据流」\n\nVue 里最顺手的是 `v-model`，双向绑定的代价是**你不太确定谁是真相之源**。React 把这个问题直接解决掉：状态只能通过 setter 改变，界面永远由状态推导。\n\n```jsx\n// ❌ 曾经的直觉：直接改\ninputRef.current.value = \"果语\";\n\n// ✅ React 的方式：改数据，界面自己跟上\nconst [name, setName] = useState(\"\");\nonChange={(e) => setName(e.target.value)}\n```\n\n一开始我很不适应，因为多了一步。但换来的是**任何一个 UI 状态都能回答「它从哪来」**——这在果语这种多组件共享主题的站点里非常值钱。\n\n## 第二个转弯 从「模板」到「函数」\n\nVue 模板是编译产物，调试时你会看到生成代码；React 里模板就是函数，调试栈与源码一一对应。这是我最喜欢的变化之一：报错信息直接指向我写的那一行。\n\n```jsx\n{posts.map((post) => (\n  <PostCard key={post.slug} post={post} />\n))}\n```\n\n代价是要自己记住 key、ref 规则这些纪律。但换来的可读性提升，在文章列表这种场景里立竿见影。\n\n## 第三个转弯 从「全局状态」到「就近状态」\n\nVuex 的思路是：既然状态难管理，那就集中起来。React 生态走了另一条路——**状态默认放在需要它的地方，只有真正跨组件才提升或用 Context**。\n\n果语里的主题就是一个好例子。它确实要跨全站，但我没有上任何状态库：\n\n```tsx\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nexport function useTheme() {\n  const ctx = useContext(ThemeContext);\n  if (!ctx) throw new Error(\"useTheme 必须在 ThemeProvider 内使用\");\n  return ctx;\n}\n```\n\n三十行代码解决的事，用 Redux 加中间件大概要八十行。**先别引入你不需要的东西**，这是这次迁移学到的最重要的一件事。\n\n## 一点体会\n\n> 框架的迁移不是 API 的翻译，而是工作流的迁移。真正的效率提升，来自把「自动」换成了「显式」。\n\nVue 2 是我 2024 年入门的工具，它的价值是把复杂的状态管理藏起来让我先跑起来。React 19 则是让我看清了那些被藏起来的部分。两者没有高下，只是阶段不同。\n\n如果你是从 Vue 转 React，最难的不是 hooks，而是**忍住不用 ref 直接改 DOM**。熬过那一周，剩下的都是水到渠成。\n"}}