惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
Cisco Talos Blog
Cisco Talos Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
V
V2EX
博客园 - 三生石上(FineUI控件)
Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
D
Docker
S
SegmentFault 最新的问题
博客园 - 聂微东
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
M
MIT News - Artificial intelligence
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The GitHub Blog
The GitHub Blog
GbyAI
GbyAI
L
LangChain Blog
Vercel News
Vercel News
博客园 - 叶小钗
MongoDB | Blog
MongoDB | Blog
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Cloudflare Blog
Engineering at Meta
Engineering at Meta
T
Threat Research - Cisco Blogs
T
Threatpost
Scott Helme
Scott Helme
T
Tailwind CSS Blog
Latest news
Latest news
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
The Register - Security
The Register - Security
罗磊的独立博客
P
Proofpoint News Feed
腾讯CDC
S
Schneier on Security
雷峰网
雷峰网
A
About on SuperTechFans
T
Tenable Blog
F
Full Disclosure
Cyberwarzone
Cyberwarzone
博客园_首页
有赞技术团队
有赞技术团队
K
Kaspersky official blog

文章列表

win or mac clash 无 TUN 让 Antigravity、Chrome 强制 proxy(解决 Antigravity 无法加载选择 model、自动更新无法登录、跳转) 【大杂烩】在 pnpm 中直接修改 node_modules(.pnpm) 中的依赖项,项目中持久化 - pnpm 中的依赖处理、幽灵依赖、寻址规则等 在 html 中直接使用 Esm、Jsx 脚本快速调试和使用 React@19 和 Vue@3 源码,解决 React19 UMD 构建等问题 一键在本地批量检测并升级更新 package.json 中的模块依赖,ncu(npm-check-updates)在 npm、pnpm 或 workspace 项目中的使用教程 解决 Mac Docker Desktop 中启动出现的问题合集 通过阿里云、腾讯云无服务器搭建自定义的企业域名邮箱,实现在 QQ邮箱 收发等功能(附腾讯 SMTP 和 IMAP) 解决使用代理(clash 等)进行 SSH 连接(如 Github ssh key clone/push)出现 kex_exchange_identification 错误 静态文件资源 cdnjs, jsdelivr 抖音字节国内快速 CDN 镜像推荐【2025】- 仍在使用 bootcdn 和 staticfile CDN 请注意验证资源的完整性(SRI) pnpm monorepo 中管理依赖的最佳实践,与 Catalogs(目录)协议的使用(monorepo 中统一版本管理) Web 安全中的 Secure Contexts(安全上下文)- 解决在本地中使用 clipboard 或 Crypto 等 API 限制或关闭上下文限制 使用 serve 配合 openssl 或 mkcert 创建本地自签名可信任的证书 - 创建本地 TLS\SSL https 协议服务 利用 Github Actions 和 Acme 自动申请、更新和部署至阿里云、腾讯云 CDN Lets Encrypt SSl\TLS ECC RSA 双证书 【CSS】解决在 flex 容器中使用 align-content 或 justify-content 属性 center 居中时的溢出滚动和截断问题 - 理解 safe 关键字 在线工具 - 一键获取下载抖音无水印视频、抖音去水印解析工具、下载抖音无水印高清图集【2025 最新】 【React Router】v6 data router 在非组件(或工具方法)中如何优雅的跳转路由 【React】为什么路由跳转时页面滚动高度不会被重置(保留上个页面高度)?理解 history scrollRestoration 的场景与使用,以及如何使用 React Router 重置和跳转前保留滚动高度 【React】在本地 Html 中快速 debug(调试)React 源码 【React】结合源码和 EventLoop 分析 - 为什么 useLayoutEffect 会阻止 DOM 重绘(而 useEffect 闪烁)?为什么其内部 useState 会“同步”执行? 在 github actions 中获取时间,并转换为中国标准时间(中国时区) 【npm】npm ci - npm clean install,在 CI、CD 中保持构建的一致性和可重复性 eslint 9.x 升级或使用指南,eslint.config.js 配置,包含 react、typescript、prettier 等常用配置升级迁移 使用 Spicetify 自定义 Spotify - 歌词翻译、全屏展示、主题替换 在 node 中快速代理请求(Proxy),解决跨域或请求转发问题 - http-proxy-middleware 修改请求体和返回 在 Hexo 中使用 AI(Gemini、deepseek、Azure)生成文章摘要,支持自定义模板。hexo-ai-summaries 插件文档(默认适配主题 Butterfly) 解决在 webstorm 或 idea 等 jetbrains 工具中遇到 Git 无法 force push,或 force push 灰色禁用无法点击(protected branches) hexo-seo-submit,Hexo 博客 SEO 优化插件 - 每日定时自动或手动提交链接至百度、Bing、Google,支持 Github Actions 和 Coding Jenkins 等CI(Hexo 插件编写) 一文吃透 pnpm 如何使用 workspace 构建 monorepo,与 npm、yarn 的用法对比(pnpm 9.x 内部安装依赖问题 link-workspace-packages) 【Node】Corepack - 解决 pnpm 或 yarn 的多版本管理、解决本地版本与 packageManager 中的版本一致性问题 解决 npm、cnpm 或 pnpm install 遇到 certificate has expired (证书已过期) npm、yarn、pnpm 设置最新国内镜像源(附官方镜像源和最新阿里源),以及 nrm 的使用教程【2025】 解决从 docker desktop 内镜像 linux 创建的容器,启动就停止、无法启动等问题(无进程容器) docker(docker desktop)中设置国内镜像源加速(阿里云、中科大),以及代理和容器代理设置,解决桌面端无法登陆就退出问题 【CSS】解决外边距重叠(重合)引起的 margin 垂直方向(top、bottom)不生效,无作用的问题 【CSS】CSS-Nesting:CSS 嵌套写法 —— 有望替代 less sass 的原生嵌套 【CSS】解决移动端(高清方案)下在谷歌浏览器中出现 字体大小布局异常,和设置的 font-size 不符(Text Autosizer、Font Boosting) 【CSS】主流 UI 库都在用的逻辑伪类选择器 not、where、is、has 【windows 11】使用 wePe 纯净 制作pe启动盘 安装\重装\升级 windows 11(windows 10)详细教程,附 windows 11 跳过联网、分区等教程 【webpack】Externals(外部扩展)浅析 - webpack 5 【Windows】一句话(一键、一分钟、一段代码)清除 Windows 11(10) 快捷方式角标(小箭头) 【Git】cherry-pick 使用场景介绍,如何在 WebStorm 中使用 cherry-pick;(从分支中提取 commit 功能) 【message-channel】了解频繁出现在框架的 Message Channel,及在事件循环(Event Loop)中的表现 【web-worker】浅析 useWorker 库如何只需函数方法即可在 worker 内运行;如何区分 Web Socket、Web Worker和Service Worker? 【React】理解学习 React 17中的批处理 与 18 批处理 为何用谷歌(Chrome)浏览器下载PDF时有时预览有时下载? 【less】Parent Selectors & 和 &&(多个父选择器的用法及回顾) 【React Router】在非组件(或工具方法)中如何优雅的跳转路由 - 理解 HashHistory 和 BrowserHistory 处理谷歌浏览器(google Chrome)https 站点将 http 资源自动转成 https - 附全站资源强制转 https vscode 国内直链接下载,提升(加速)下载速度 【css-tricks】从 flex 子元素超出父级(容器)宽度,探其原理 flex:1 时 width 的作用 linux,mac 终端(Terminal)上使用代理(http/https/socks5)提升速度,给 git 一键设置代理提升速度 修复 Win 11或10 Xbox Game Bar 录屏截屏打不开灰色等 ios,Windows 网页皆可平滑、惯性滚动(Smooth-scroll.js)- 博客、网站、web、JavaScript 【2022年】apple 苹果(ios)注册美服区(日区)ID教程(附免税地区及身份信息生成器)-时效:2022年2月
React Compiler - 解放在函数中编程时的性能焦虑(React Conf 2024)附 Next 在线演示
kshao · 2024-06-25 · via

注意 React Compiler 目前仍处于实验阶段,需要 React 19 Beta 不建议在生产中使用~。(可观看在 React Conf 中的介绍)

https://react.dev/learn/react-compiler

使用场景(为什么要使用?)

在平时开发中,我们经常会因为避免不必要或不是预期的 rerenderpropsstatecontext) 或缓存一些计算结果,而使用 React.memouseMemouseCallback 等记忆函数,例如:

props 变化导致的 rerender

当父组件 rerender 后,若子组件未使用 React.memo,则子组件也会 rerender。然而当子组件使用 React.memo 后,若 props 中的某个属性为一个匿名函数或对象时,子组件仍会 rerender

const ChildComponent = (props) => {
  const { count, onChange, config } = props;

  console.log('render', config);

  return (
    <div>
      <button onClick={onChange}>click</button>
      <span>{count}</span>
    </div>
  );
};

const Page = () => {
  const [count, setCount] = useState(0);
  const [number, setNumber] = useState(0);

  return (
    <div>
      <ChildComponent
        count={count}
        onChange={() => console.log(123)}
        config={{ someConfig: 'some cinfig here' }}
      />
    </div>
  );
};

手动避免 rerender

为什么使用了 memo 还会触发 renderReact 中的 memo 依赖相关比较都是浅比较,当 props 中的某个属性为一个匿名函数或对象时,memo 会认为 props 发生了变化,从而触发 render

const objectIs: (x: any, y: any) => boolean =
  // $FlowFixMe[method-unbinding]
  typeof Object.is === 'function' ? Object.is : is;

function areHookInputsEqual(nextDeps, prevDeps) {
  //...
  // $FlowFixMe[incompatible-use] found when upgrading Flow
  for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
    // $FlowFixMe[incompatible-use] found when upgrading Flow
    if (objectIs(nextDeps[i], prevDeps[i])) {
      continue;
    }
    return false;
  }

  return true;
}

所以,我们在使用 React.memo 的同时,也需要在父组件中使用 useMemouseCallback 来避免不必要的 render

import { useMemo, useCallback, memo } from 'react';

const ChildComponent = memo((props) => {
  const { count, onChange, config } = props;

  console.log('render', config);

  return (
    <div>
      <button onClick={onChange}>click</button>
      <span>{count}</span>
    </div>
  );
});

const Page = () => {
  const [count, setCount] = useState(0);
  const [number, setNumber] = useState(0);

  const handleChange = useCallback(() => {
    console.log(123);
  }, []);

  const config = useMemo(() => {
    return { someConfig: 'some cinfig here' };
  }, []);

  return (
    <div>
      <ChildComponent count={count} onChange={handleChange} config={config} />
    </div>
  );
};

Checking compatibility - 检查老项目是否兼容使用 React Compiler

在上述描述后,或者平时在开发中早已被这些 “Memo” 产生了无形的性能焦虑。可以使用 react 提供的 healthcheck 在已有项目中尝试~ 可以先检查代码是否兼容。

npx react-compiler-healthcheck@latest

该脚本将会检查:

  • 多少组件可以成功优化
  • 检查是否使用了 <StrictMode>,启用 <StrictMode> 后,编译器的优化将更为轻松,说明你的项目已经在遵循 React 规则
  • 检查不兼容的库使用。
Successfully compiled 8 out of 9 components.
StrictMode usage not found.
Found no usage of incompatible libraries.

配置 eslint-plugin-react-compiler

React Compiler 提供了 eslint 插件,用于检查代码是否符合优化规则,且独立于 React Compiler。当该插件显示你的代码有违反 React Rules 时,编译器同样也会跳过优化。

例如,修改 props 的值。

pnpm install eslint-plugin-react-compiler

配置 .eslintrc.js

暂未提供 9.x

.eslintrc.js

module.exports = { plugins: ['eslint-plugin-react-compiler'], rules: { 'react-compiler/react-compiler': 'error', }, };

配置 react compiler

sources

在指定文件夹中优化。

const ReactCompilerConfig = {
  sources: (filename) => {
    return filename.indexOf('src/path/to/dir') !== -1;
  },
};

compilationMode

支持 annotation、infer、syntax、all,当你只需要在某些组件或 hooks 中优化时,可以使用 annotation 模式,并在组件或 hooks 中添加 use memo 注释。

const ReactCompilerConfig = {
  compilationMode: 'annotation', // annotation | infer | syntax | all
};

// src/app.jsx
export default function App() {
  'use memo';
  // ...
}

或者只需要反选某些不需要的组件。需要注意的是,注释模式并不在长久计划内。

const ReactCompilerConfig = {
  compilationMode: 'all', // annotation | infer | syntax | all
};

// src/app.jsx
export default function App() {
  'use no memo';
  // ...
}

logger

目前该属性并没有明确的文档说明,但通过源码解读时,发现可以通过该属性来定义日志输出。

const ReactCompilerConfig = {
  logger: {
    logEvent: (fileName, event) => {
      console.log(fileName, event, 'reactCompiler');
    },
  },
};

在现有项目中使用 React Compiler

React Compiler 提供了 Babel 插件,将其添加至 babel 配置中即可。注意,插件需要在其他插件之前运行。

pnpm install babel-plugin-react-compiler

babel.config.js

const ReactCompilerConfig = {}; module.exports = function () { return { plugins: [ ['babel-plugin-react-compiler', ReactCompilerConfig], // must run first! // ... ], }; };

配置成功后,在运行或构建时。

在 Next 中使用 React Compiler

Next RC 15 中,支持 React Compiler,升级后,只需要在 next.config.js 中添加 reactCompiler 配置即可。

pnpm install next@canary react@canary react-dom@canary babel-plugin-react-compiler

next.config.ts

const nextConfig = { experimental: { // reactCompiler: true, reactCompiler: { compilationMode: 'annotation', }, }, }; module.exports = nextConfig;

配置成功后,可在 React Devtools 中发现 Memo 标志

源码浅析

function ChildComponent(props) {
  const { count, onChange, config } = props;

  console.log('render', config);

  return (
    <div>
      <button onClick={onChange}>click</button>
      <span>{count}</span>
    </div>
  );
}

function Page() {
  const [count, setCount] = useState(0);
  const [number, setNumber] = useState(0);

  return (
    <div>
      <ChildComponent
        count={count}
        onChange={() => console.log(123)}
        config={{ someConfig: 'some cinfig here' }}
      />
    </div>
  );
}
function ChildComponent(props) {
  const $ = _c(7);

  const { count, onChange, config } = props;
  console.log('render', config);
  let t0;

  if ($[0] !== onChange) {
    t0 = <button onClick={onChange}>click</button>;
    $[0] = onChange;
    $[1] = t0;
  } else {
    t0 = $[1];
  }

  let t1;

  if ($[2] !== count) {
    t1 = <span>{count}</span>;
    $[2] = count;
    $[3] = t1;
  } else {
    t1 = $[3];
  }

  let t2;

  if ($[4] !== t0 || $[5] !== t1) {
    t2 = (
      <div>
        {t0}
        {t1}
      </div>
    );
    $[4] = t0;
    $[5] = t1;
    $[6] = t2;
  } else {
    t2 = $[6];
  }

  return t2;
}

function Page() {
  const $ = _c(4);

  const [count] = useState(0);
  useState(0);
  let t0;
  let t1;

  if ($[0] === Symbol.for('react.memo_cache_sentinel')) {
    t0 = () => console.log(123);

    t1 = {
      someConfig: 'some cinfig here',
    };
    $[0] = t0;
    $[1] = t1;
  } else {
    t0 = $[0];
    t1 = $[1];
  }

  let t2;

  if ($[2] !== count) {
    t2 = (
      <div>
        <ChildComponent count={count} onChange={t0} config={t1} />
      </div>
    );
    $[2] = count;
    $[3] = t2;
  } else {
    t2 = $[3];
  }

  return t2;
}

解析

React Compiler 并不是对 props 或相关变量增加 useMemouseCallback,而是将 props 或相关变量的值缓存至一个变量中(在测试时发现同一个 变量 被存了多份,暂不知是设计如此还是?),当值发生变化时,重新渲染,反之取缓存。
与其他优化相比,使用 Babel 后的 React Compiler 实现了更细粒度的优化,能精确地实现最小化更新。

react/compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Program.ts

function compileProgram(program: NodePath<t.Program>, pass: CompilerPass) { const useMemoCacheIdentifier = program.scope.generateUidIdentifier('c'); // .... compiledFn = compileFn( fn, config, fnType, useMemoCacheIdentifier.name, pass.opts.logger, pass.filename, pass.code, ); }

react/compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts

function codegenFunction(fn: ReactiveFunction, uniqueIdentifiers: Set<string>) { // .... // 共缓存的变量 数量 => // const $ = _c(4); // t0 = $[0]; const cacheCount = compiled.memoSlotsUsed; // The import declaration for `useMemoCache` is inserted in the Babel plugin preface.push( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier(cx.synthesizeName('$')), t.callExpression(t.identifier(fn.env.useMemoCacheIdentifier), [ t.numericLiteral(cacheCount), ]), ), ]), ); if (fastRefreshState !== null) { // ... } }

https://playground.react.dev/
https://react.dev/learn/react-compiler