Featured image of post React 编译器:告别手动记忆Featured image of post React 编译器:告别手动记忆

React 编译器:告别手动记忆

自动构建时编译器如何处理列表和回调的反应性渲染优化。

介绍

多年来,React 开发人员一直使用 useMemouseCallbackReact.memo 和各种状态管理启发式方法手动优化重新渲染。 React Compiler(以前称为 React Forget)完全改变了这种范例。它是一个构建时工具,通过在编译时分析反应依赖关系来自动记忆 React 组件、钩子和值,从而无需手动优化钩子。


手动记忆的问题

考虑一个典型的过滤列表组件:

function UserList({ users, filter }) {
  const filtered = useMemo(
    () => users.filter(u => u.name.includes(filter)),
    [users, filter]
  );

  const handleClick = useCallback((id) => {
    console.log('Clicked:', id);
  }, []);

  return (
    <ul>
      {filtered.map(user => (
        <UserRow key={user.id} user={user} onClick={handleClick} />
      ))}
    </ul>
  );
}

const UserRow = React.memo(({ user, onClick }) => (
  <li onClick={() => onClick(user.id)}>{user.name}</li>
));

这是可行的,但它很冗长,容易出错(依赖项数组丢失或不正确),并且很容易忘记。大列表中缺少 useMemo 会导致不必要的重新渲染;不正确的依赖数组会导致陈旧的闭包。


什么是 React 编译器?

React Compiler 是一个构建时插件(Babel / SWC),可以自动理解 React 组件的反应性模型。它会自动转译您的代码以包含记忆逻辑:

// You write this:
function Welcome({ name }) {
  const greeting = `Hello, ${name}!`;
  return <p>{greeting}</p>;
}
// Compiler outputs effectively this:
function Welcome({ name }) {
  const $ = React.cache(/* ... */);
  let greeting = $.refresh('greeting', name => `Hello, ${name}!`, [name]);
  return $.memo('element', () => <p>{greeting}</p>, [greeting]);
}

编译器会检测哪些值依赖于 props、state 或其他反应值,并且仅在这些依赖项发生变化时重新计算它们。


它是如何运作的

编译器分三个阶段运行:

  1. 反应性值检测 — 它遍历抽象语法树 (AST),以识别哪些变量源自 props、state、hooks 或其他反应性源。

  2. 依赖图构建 — 它构建连接反应值的依赖图。任何仅源自非反应源的值均被视为静态值。

  3. 记忆化代码生成 — 它生成具有内置缓存失效功能的优化 JavaScript。运行时使用类似于信号的细粒度依赖跟踪机制。

编译器遵循的关键规则:

规则行为
Props 和 state 始终是反应性的任何派生值都会自动记忆
非反应常数被提升没有增加记忆开销
回调默认稳定不需要 useCallback
组件主体优化仅当反应性依赖发生变化时才重新执行

与构建工具集成

维特

// vite.config.js
import react from '@vitejs/plugin-react';

export default {
  plugins: [
    react({
      babel: {
        plugins: [['babel-plugin-react-compiler', { target: '18' }]],
      },
    }),
  ],
};

Next.js

// next.config.js
module.exports = {
  experimental: {
    reactCompiler: true,
  },
};

你可以删除什么

启用 React 编译器后,您不再需要:

  • useMemo() — 自动值记忆
  • useCallback() — 自动功能稳定性
  • React.memo() — 自动组件记忆(编译器包装渲染输出)
  • 大多数情况下手动依赖数组

编译器甚至可以优化难以手动处理的模式,例如条件挂钩 - 尽管您仍然应该遵循挂钩规则。


注意事项和最佳实践

虽然编译器可以处理绝大多数情况,但某些情况需要谨慎:

警告推荐
可变引用useRef 突变未被跟踪使用 useState 或触发显式重新渲染
外部存储 — 值不是来自 React包裹在 useSyncExternalStore
复杂的副作用useEffect 具有非反应性依赖如果需要,添加显式依赖
遗留上下文 — 类组件上下文迁移到现代 useContext

该编译器对于遵循标准 React 模式的代码库最为有效。高度命令式代码可能带来的好处较少。


结论

React 编译器代表了从手动自动记忆的根本转变。通过在构建时分析反应性依赖关系,它消除了整个类别的性能错误和样板文件。对于现有项目,启用编译器需要最少的配置;对于新项目,它改变了你对 React 性能的看法——你只需编写声明性组件,然后让编译器处理其余的事情。