React Compiler

  • React Compiler 是目前在 RC 中的一个新编译器, 会自动记住你的代码, 避免忘记或者错误的使用 useMemouseCallbackReact.memoReact 的 API

项目地址

创建 React Compiler 应用

新建一个项目

1
2
3
4
5
6
7
8
9
10
11
# 通过 vite 创建项目
npm create vite@latest

# Project name:
react-compiler

# Select a framework:
React

# Select a variant: (这里一定要选择 TypeScript, 因为 TypeScript+SWC 还没有适配)
TypeScript

安装配置依赖

  • 安装 babel-plugin-react-compilereslint-plugin-react-hooks

    1
    2
    npm install --save-dev --save-exact babel-plugin-react-compiler@rc
    npm install --save-dev eslint-plugin-react-hooks@^6.0.0-rc.1
  • vite.config.ts 中配置 babel-plugin-react-compiler

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import { defineConfig } from 'vite';
    import react from '@vitejs/plugin-react';

    + const ReactCompilerConfig = {
    + target: '19', // '17' | '18' | '19'
    + };

    // https://vite.dev/config/
    export default defineConfig({
    plugins: [
    react({
    + babel: {
    + plugins: [['babel-plugin-react-compiler', ReactCompilerConfig]],
    + },
    }),
    ],
    });
  • eslint.config.js 中配置 eslint-plugin-react-hooks

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    import js from '@eslint/js';
    import globals from 'globals';
    import reactHooks from 'eslint-plugin-react-hooks';
    import reactRefresh from 'eslint-plugin-react-refresh';
    import tseslint from 'typescript-eslint';

    export default tseslint.config(
    { ignores: ['dist'] },
    {
    extends: [js.configs.recommended, ...tseslint.configs.recommended],
    files: ['**/*.{ts,tsx}'],
    languageOptions: {
    ecmaVersion: 2020,
    globals: globals.browser,
    },
    plugins: {
    - 'react-hooks': reactHooks,
    + 'react-hooks': reactHooks.configs.recommended,
    'react-refresh': reactRefresh,
    },
    rules: {
    ...reactHooks.configs.recommended.rules,
    'react-refresh/only-export-components': [
    'warn',
    { allowConstantExport: true },
    ],
    },
    }
    );

怎么知道 React Compiler 是否生效 ?

对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/** 每次 render 都会重新渲染 **/
const TestMemo = () => {
console.log('TestMemo');
return <p>TestMemo</p>;
};

/** 每次 render 都会重新渲染 **/
const TestMemoCallback = ({ onClick }: any) => {
console.log('TestMemoCallback', onClick);
return <button onClick={onClick}>TestMemoCallback</button>;
};

/** 每次 render 都会重新渲染 **/
const TestMemoUseMemo = ({ arr }: any) => {
console.log('TestMemoUseMemo');
return <p>{arr}</p>;
};

function App() {
const [count, setCount] = useState(0);

const handleClick = () => {};

const arr = [1, 2, 3];

return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
</div>

<TestMemo />
<TestMemoCallback onClick={handleClick} />
<TestMemoUseMemo arr={arr} />
</>
);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* memo
* - 如果 nextProps 与 nextState 相同,则组件不重新渲染
* - 即使 const TestMemo = memo(({ otherState })=> {}); 也不会重新渲染, 因为父组件中变更的state与otherState无关
* - 只有当父组件中otherState发生变化时,才会重新渲染
*/
const TestMemo = memo(() => {
console.log('TestMemo');
return <p>TestMemo</p>;
});

/**
* memo + useCallback
* - useCallback 缓存函数,避免每次渲染都生成新的函数
*/
const TestMemoCallback = memo(({ onClick }: any) => {
console.log('TestMemoCallback', onClick);
return <button onClick={onClick}>TestMemoCallback</button>;
});

/**
* memo + useMemo
* - useMemo 缓存计算结果,避免每次渲染都重新计算
*/
const TestMemoUseMemo = memo(({ arr }: any) => {
console.log('TestMemoUseMemo');
return <p>{arr}</p>;
});

function App() {
const [count, setCount] = useState(0);

const handleClick = useCallback(() => {}, []);

const arr = useMemo(() => [1, 2, 3], []);

return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
</div>

<TestMemo />
<TestMemoCallback onClick={handleClick} />
<TestMemoUseMemo arr={arr} />
</>
);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/** 编译器自动优化 */
const TestMemo = () => {
console.log('TestMemo');
return <p>TestMemo</p>;
};

/** 编译器自动优化 */
const TestMemoCallback = ({ onClick }: any) => {
console.log('TestMemoCallback', onClick);
return <button onClick={onClick}>TestMemoCallback</button>;
};

/** 编译器自动优化 */
const TestMemoUseMemo = ({ arr }: any) => {
console.log('TestMemoUseMemo');
return <p>{arr}</p>;
};

function App() {
const [count, setCount] = useState(0);

// 编译器自动优化
const handleClick = () => {};

// 编译器自动优化
const arr = [1, 2, 3];

return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
</div>

<TestMemo />
<TestMemoCallback onClick={handleClick} />
<TestMemoUseMemo arr={arr} />
</>
);
}

在线示例

React Normal

React Memo

React Compiler


参考文章:【鸣谢】

React Compiler

React Compiler RC