{} === {} // false因此,高级 React 开发者需要非常了解 React 的默认优化机制,让 props 的比较不发生,因为一旦发生,那么结果必定是 false。
<StrictMode> <BrowserRouter> <App /> </BrowserRouter> </StrictMode>遵循严格模式的规范,我们的组件更容易符合 React Compiler 的优化规则。我们可以使用如下方式首先检测代码库是否兼容。在项目根目录下执行如下指令。
npx react-compiler-healthcheck该脚本主要用于检测
const ReactCompilerConfig = { sources: (filename) => { return filename.indexOf('src/path/to/dir') !== -1; }, };React Compiler 还支持对应的 eslint 插件。该插件可以独立运行。不用非得与 Compiler 一起运行。
npm i eslint-plugin-react-compiler然后在 eslint 的配置中添加
module.exports = { plugins: [ 'eslint-plugin-react-compiler', ], rules: { 'react-compiler/react-compiler': 2, }, }Compiler 目前结合 Babel 插件一起使用,因此,我们首先需要在项目中引入该插件
npm i babel-plugin-react-compiler然后,在不同的项目中,有不同的配置。
module.exports = function () { return { plugins: [ ['babel-plugin-react-compiler', ReactCompilerConfig], // must run first! // ... ], }; };
注意,该插件应该在其他 Babel 插件之前运行。
在 vite 中使用
首先,我们需要安装 vite-plugin-react,注意不用搞错了,群里有的同学使用了 vite-plugin-react-swc 结果搞了很久没配置成功。然后在 vite.config.js 中,添加如下配置export default defineConfig(() => { return { plugins: [ react({ babel: { plugins: [ ["babel-plugin-react-compiler", ReactCompilerConfig], ], }, }), ], // ... }; });在 Next.js 中使用
npm i vite-plugin-babel // vite.config.js import babel from "vite-plugin-babel"; // 堆代码 duidaima.com const ReactCompilerConfig = { /* ... */ }; export default defineConfig({ plugins: [ remix({ /* ... */}), babel({ filter: /\.[jt]sx?$/, babelConfig: { presets: ["@babel/preset-typescript"], // if you use TypeScript plugins: [ ["babel-plugin-react-compiler", ReactCompilerConfig], ], }, }), ], });在 Webpack 中使用
const ReactCompilerConfig = { /* ... */ }; const BabelPluginReactCompiler = require('babel-plugin-react-compiler'); function reactCompilerLoader(sourceCode, sourceMap) { // ... const result = transformSync(sourceCode, { // ... plugins: [ [BabelPluginReactCompiler, ReactCompilerConfig], ], // ... }); if (result === null) { this.callback( Error( `Failed to transform "${options.filename}"` ) ); return; } this.callback( null, result.code result.map === null ? undefined : result.map ); } module.exports = reactCompilerLoader;我们可以在 React 官方了解到更多关于 React Compiler 的介绍与注意事项。具体地址如下:https://react.dev/learn/react-compiler
<script src="http://localhost:8097"></script>
function Index() { const [counter, setCounter] = useState(0) function p() { console.log('函数执行 ') } return ( <div> <button onClick={() => setCounter(counter + 1)}> 点击修改 counter:{counter} </button> <Children a={1} b={2} c={p} /> </div> ) }我们先来分析一下这段代码。首先,在父组件中,我们设计了一个数字递增的状态。当点击发生时,状态递增。此时父组件会重新 render。因此,在以往的逻辑中,子组件 Children 由于没有使用任何优化手段,因此,在父组件重新渲染时,子组件由于 props 的比较结果为 false,也会重新执行。并且其中一个 props 属性还是一个引用对象,因此我们需要使用 useCallback + memo 才能确保子组件 Children 不会冗余 re-render。
import {use} from 'react' import {Context} from './context' export default function Card() { const value = use(Context) console.log('xxxxx context') return ( <> <div className='_06_card'> <div className="title">Canary</div> <p>The test page</p> </div> </> ) }理想情况是这种情况可以不用发生 re-render。因此总体来说,Compiler 目前确实还不能完全信任。也有可能我还没掌握正确的姿势,还需要对他有更进一步的了解才可以。不过值得高兴的是,新项目可以放心使用 Compiler,因为运行结果我们都能实时感知、调试、调整,能最大程度的避免问题的出现。