React 生命周期全景图:从类组件到 Hooks,一次看懂 2025 实战用法

生命周期不是八股文,而是性能卡点、Bug 源头、优化入口

本文用「类组件」+「Hooks」两条主线,把生命周期放到 真实业务场景 里讲透:首屏加载、长列表、弹窗动画、热更新、异常兜底......看完就能直接落地。


一、总览:两条生命周期路线

版本 生命周期形式 2025 推荐度
类组件 constructor → render → componentDidMount/Update/Unmount ⚠️ 老项目维护
函数组件 useState → useEffect → useLayoutEffect → useInsertionEffect ✅ 新项目首选

记住口诀:函数组件优先,类组件救命


二、类组件生命周期 × 4 大场景

场景 1:首屏骨架屏 + 数据并行加载

jsx 复制代码
class ArticlePage extends React.Component {
  state = { article: null, loading: true };

  // 1️⃣ 构造函数:只做一次初始化
  constructor(props) {
    super(props);
    this.id = props.match.params.id;
  }

  // 2️⃣ 挂载阶段:并行拉文章 & 推荐列表
  async componentDidMount() {
    // 并行 Promise.all,骨架屏已占位
    const [article, recommends] = await Promise.all([
      fetchArticle(this.id),
      fetchRecommends(this.id)
    ]);
    this.setState({ article, recommends, loading: false });
  }

  // 3️⃣ 更新阶段:路由变化重新拉数据
  async componentDidUpdate(prevProps) {
    if (prevProps.match.params.id !== this.props.match.params.id) {
      this.setState({ loading: true });
      const article = await fetchArticle(this.props.match.params.id);
      this.setState({ article, loading: false });
    }
  }

  // 4️⃣ 卸载阶段:取消未完成的请求
  componentWillUnmount() {
    this.controller?.abort();
  }

  render() {
    const { article, loading } = this.state;
    return loading ? <Skeleton /> : <ArticleDetail {...article} />;
  }
}

要点

  • componentDidMount 做首屏并行请求,骨架屏兜底。
  • componentWillUnmount 清掉请求,防止内存泄漏。

场景 2:长列表滚动加载 + 防抖

jsx 复制代码
class ScrollList extends React.Component {
  state = { list: [], page: 1, loading: false };

  componentDidMount() {
    this.loadMore();
    window.addEventListener('scroll', this.handleScroll);
  }

  componentWillUnmount() {
    window.removeEventListener('scroll', this.handleScroll);
  }

  handleScroll = throttle(() => {
    if (this.isBottom()) this.loadMore();
  }, 200);

  loadMore = async () => {
    this.setState({ loading: true });
    const newData = await fetchList(this.state.page);
    this.setState(prev => ({
      list: [...prev.list, ...newData],
      page: prev.page + 1,
      loading: false
    }));
  };

  render() {
    return (
      <div>
        {this.state.list.map(item => <Card key={item.id} {...item} />)}
        {this.state.loading && <Spinner />}
      </div>
    );
  }
}

要点

componentDidMount 加滚动事件,在 componentWillUnmount 移除,避免内存泄漏。


三、函数组件 Hooks × 4 大场景

场景 3:弹窗动画 + 焦点管理(useLayoutEffect)

jsx 复制代码
function Modal({ visible, children }) {
  const ref = useRef(null);

  // 1️⃣ 同步阶段:测量 DOM → 立即计算动画起点
  useLayoutEffect(() => {
    if (visible) {
      // 读取布局 → 写回 transform
      const { height } = ref.current.getBoundingClientRect();
      ref.current.style.transform = `translateY(${-height}px)`;
    }
  }, [visible]);

  // 2️⃣ 异步阶段:动画结束后清理
  useEffect(() => {
    if (visible) {
      ref.current?.focus();
      return () => ref.current?.blur();
    }
  }, [visible]);

  return visible ? <div ref={ref}>{children}</div> : null;
}

要点

  • useLayoutEffect 在浏览器绘制前执行,防止动画抖动。
  • useEffect 负责副作用(聚焦、清理)。

场景 4:热更新保留滚动位置(useInsertionEffect)

jsx 复制代码
function ChatWindow() {
  const [messages, setMessages] = useState([]);
  const listRef = useRef(null);

  // 只在 Fast-Refresh 期间插入样式,不影响渲染
  useInsertionEffect(() => {
    document.head.insertAdjacentHTML('beforeend', `
      <style>.chat-scroll { scroll-behavior: smooth }</style>
    `);
  }, []);

  useEffect(() => {
    listRef.current.scrollTop = listRef.current.scrollHeight;
  }, [messages]);

  return (
    <ul ref={listRef} className="chat-scroll">
      {messages.map(m => <li key={m.id}>{m.text}</li>)}
    </ul>
  );
}

四、异常兜底:Error Boundary & useErrorBoundary

jsx 复制代码
class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // 上报 Sentry
    logError(error, info);
  }

  render() {
    return this.state.hasError ? <Fallback /> : this.props.children;
  }
}

场景:路由级、模块级异常兜底,生产环境必备。


五、2025 选型建议

场景 推荐
新项目 函数组件 + Hooks(useEffect/LayoutEffect)
老代码 类组件生命周期维护,逐步迁移
性能敏感 使用 React.memouseMemouseCallback 减少重渲染

🏁 一页速查表

生命周期 类组件 函数组件 典型用途
挂载 componentDidMount useEffect(..., []) 数据请求、事件绑定
更新 componentDidUpdate useEffect(..., [deps]) 数据刷新
卸载 componentWillUnmount useEffect return 函数 清理、取消订阅
动画 useLayoutEffect 同步 DOM 读/写
异常 componentDidCatch ErrorBoundary 全局兜底

把这张表贴在工位,React 生命周期不再迷路!

相关推荐
OpenTiny社区12 分钟前
一文解读“Performance面板”前端性能优化工具基础用法!
前端·性能优化·opentiny
拾光拾趣录33 分钟前
🔥FormData+Ajax组合拳,居然现在还用这种原始方式?💥
前端·面试
不会笑的卡哇伊43 分钟前
新手必看!帮你踩坑h5的微信生态~
前端·javascript
bysking44 分钟前
【28 - 记住上一个页面tab】实现一个记住用户上次点击的tab,上次搜索过的数据 bysking
前端·javascript
Dream耀1 小时前
跨域问题解析:从同源策略到JSONP与CORS
前端·javascript
前端布鲁伊1 小时前
【前端高频面试题】面试官: localhost 和 127.0.0.1有什么区别
前端
HANK1 小时前
Electron + Vue3 桌面应用开发实战指南
前端·vue.js
胤祥矢量商铺1 小时前
菜鸟笔记007 [...c(e), ...d(i)]数组的新用法
c语言·开发语言·javascript·笔记·illustrator插件
極光未晚1 小时前
Vue 前端高效分包指南:从 “卡成 PPT” 到 “丝滑如德芙” 的蜕变
前端·vue.js·性能优化
郝亚军1 小时前
炫酷圆形按钮调色器
前端·javascript·css