--- tags: [React, Migration, Upgrade, Frontend] create time: 2026-04-29 22:17 --- # 迁移与升级 ## 概述 大型 React 项目不可避免地面临技术债务和版本升级。本文档提供从 Class 组件到函数组件、从旧版 Router 到 v7、以及应对 React Major Version 变更的系统化迁移策略。 ## Class → Function + Hooks 迁移 ### 逐步对照表 | Class 特性 | Hook 等效写法 | |-----------|--------------| | `this.state` | `useState` | | `componentDidMount` | `useEffect(() => { ... }, [])` | | `componentDidUpdate(prev)` | `useEffect(() => { ... }, [dep])` | | `componentWillUnmount` | `useEffect(() => { return () => { cleanup } })` | | `this.setState(fn)` | `setState(prev => ({ ... }))` | | `ref = React.createRef()` | `useRef` | | `shouldComponentUpdate` | `React.memo` / `useMemo` / `useCallback` | | Context.Consumer | `useContext` | ### 实际迁移示例 ```tsx // ❌ Before: Class Component class UserProfile extends React.Component<{ userId: string }> { state = { user: null, loading: true }; componentDidMount() { fetchUser(this.props.userId).then(user => { this.setState({ user, loading: false }); }); } componentDidUpdate(prevProps) { if (prevProps.userId !== this.props.userId) { this.setState({ loading: true }); fetchUser(this.props.userId).then(user => this.setState({ user, loading: false })); } } componentWillUnmount() { // nothing to clean up here } render() { const { user, loading } = this.state; return loading ? :
{user.name}
; } } // ✅ After: Functional Component + Hooks function UserProfile({ userId }: { userId: string }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { let cancelled = false; setLoading(true); fetchUser(userId).then(u => { if (!cancelled) setUser(u); }).finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; // cleanup }, [userId]); // userId 变化时自动重新 fetch if (loading) return ; return
{user?.name}
; } ``` > [!warning] 迁移 Checklist > - [ ] 所有 `componentDidMount/Update/Unmount` 转译为 useEffect > - [ ] State 合并:将互不相关的 state 拆分,减少不必要的重渲染 > - [ ] 移除 `this.` —— 检查所有闭包中的变量引用 > - [ ] Props 类型从 class props 改为 interface 解构 ## React Router v6 → v7 迁移 ```mermaid graph LR A["v6 BrowserRouter"] -->|"Switch → Routes"| B["v7 Routes"] C["v6 "] -->|"path prop 简化"| D["v7 { path: '/', element: {} }"] E["v6 "] -->|"navigate(-1)/replace"| F["v7 "] G["v6 Outlet"] -->|"保留"| H["v7 Outlet — 位置不变"] style A fill:#F4DBD6,color:#000 style B fill:#61DAFB,color:#000 ``` ### API 映射速查 | v6 | v7 | 备注 | |----|----|------| | `` | `` + `createBrowserRouter` | data router 模式 | | `Switch` | `Routes` | v6 已有 | | `` | `` | 语义更清晰 | | `useHistory` | `useNavigate` | 名称统一 | | `` | `} >` | JSX 方式 | | `match.params` | `useParams()` | hook 方式 | ## React 17 → 18 兼容性 | 注意事项 | 说明 | |----------|------| | `ReactDOM.render` 已废弃 | 改用 `createRoot().render()` | | `ReactDOM.unmountComponentAtNode` 已废弃 | 改用 `root.unmount()` | | 事件处理在微任务中执行 | `event.persist()` 已移除 | | StrictMode 双重渲染 | 开发环境 effect 先 mount → unmount → remount(用于检测) | ```tsx // React 17 import ReactDOM from "react-dom"; ReactDOM.render(, document.getElementById("root")); // React 18 import { createRoot } from "react-dom/client"; const root = createRoot(document.getElementById("root")); root.render(); ``` ## Next.js Pages → App Router 迁移 | Pages Router | App Router | |-------------|-----------| | `pages/` 目录 | `app/` 目录 | | `_document.tsx` | `app/layout.tsx` | | `_app.tsx` | `app/layout.tsx`(根布局) | | `getServerSideProps` | async Page 组件 | | `getStaticProps` | async Page + `revalidate` | | `next/router` | `next/navigation` | | `_middleware.ts` | `middleware.ts` | ```tsx // Pages Router export async function getServerSideProps() { const data = await api.getData(); return { props: { data } }; } // App Router async function Page() { const data = await api.getData(); return ; } ``` ## Major Version 升级通用流程 ```mermaid flowchart TD A["发现新版本发布"] --> B["阅读 Changelog / Breaking Changes"] B --> C{"影响范围评估"} C --> D["仅 minor version bump"] C --> E["API 变更 / 新配置项"] C --> F["架构性改动"] D --> G["直接升级 + CI 验证"] E --> H["逐个文件修复"] F --> I["分阶段迁移 + 并行验证"] G --> J["更新依赖 + 运行测试"] H --> J I --> J J --> K["Staging 环境验证"] K --> L["灰度发布 / Feature Flag"] L --> M["全量上线"] style F fill:#F5A87D,color:#000 style I fill:#F5A87D,color:#000 ``` ## 关联笔记