This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
all-in-kingsoft/hhs/REACT/5. 工程实践篇/18-迁移与升级.md
T
2026-04-29 22:19:51 +08:00

5.3 KiB

tags, create time
tags create time
React
Migration
Upgrade
Frontend
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

实际迁移示例

// ❌ 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 ? <Spinner /> : <div>{user.name}</div>;
  }
}

// ✅ After: Functional Component + Hooks
function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState<User | null>(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 <Spinner />;
  return <div>{user?.name}</div>;
}

[!warning] 迁移 Checklist

  • 所有 componentDidMount/Update/Unmount 转译为 useEffect
  • State 合并:将互不相关的 state 拆分,减少不必要的重渲染
  • 移除 this. —— 检查所有闭包中的变量引用
  • Props 类型从 class props 改为 interface 解构

React Router v6 → v7 迁移

graph LR
    A["v6 BrowserRouter"] -->|"Switch → Routes"| B["v7 Routes"]
    C["v6 <Route path='/' element={} />"] -->|"path prop 简化"| D["v7 { path: '/', element: {} }"]
    E["v6 <Redirect/>"] -->|"navigate(-1)/replace"| F["v7 <Navigate replace />"]
    G["v6 Outlet"] -->|"保留"| H["v7 Outlet — 位置不变"]
    
    style A fill:#F4DBD6,color:#000
    style B fill:#61DAFB,color:#000

API 映射速查

v6 v7 备注
<BrowserRouter> <RouterProvider> + createBrowserRouter data router 模式
Switch Routes v6 已有
<Redirect to="/x" /> <Navigate to="/x" /> 语义更清晰
useHistory useNavigate 名称统一
<Route component={Page}> <Route element={<Page />} > JSX 方式
match.params useParams() hook 方式

React 17 → 18 兼容性

注意事项 说明
ReactDOM.render 已废弃 改用 createRoot().render()
ReactDOM.unmountComponentAtNode 已废弃 改用 root.unmount()
事件处理在微任务中执行 event.persist() 已移除
StrictMode 双重渲染 开发环境 effect 先 mount → unmount → remount(用于检测)
// React 17
import ReactDOM from "react-dom";
ReactDOM.render(<App />, document.getElementById("root"));

// React 18
import { createRoot } from "react-dom/client";
const root = createRoot(document.getElementById("root"));
root.render(<App />);

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
// 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 <View data={data} />;
}

Major Version 升级通用流程

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

关联笔记