FEATURED · 精选文章

Babel 插件 @babel/plugin-transform-react-pure-annotations:为 React 顶层方法调用标记纯函数注解以优化 Tree Shaking

发布时间 / 2026/9/19 22:24:31
来源 / 创域科博编辑部
栏目 / 资讯中心
Babel 插件 @babel/plugin-transform-react-pure-annotations:为 React 顶层方法调用标记纯函数注解以优化 Tree Shaking Babel 插件 babel/plugin-transform-react-pure-annotations为 React 顶层方法调用标记纯函数注解以优化 Tree Shaking【免费下载链接】babel Babel is a compiler for writing next generation JavaScript.项目地址: https://gitcode.com/gh_mirrors/ba/babel导读本文围绕 Babel 仓库中的 packages/babel-plugin-transform-react-pure-annotations/README.md 展开深入讲解该插件将 React 顶层方法调用标记为纯pure的机制它通过向React.createElement、React.memo、ReactDOM.createPortal等调用前注入/*#__PURE__*/注解让 Terser 等压缩器在死代码消除dead code elimination时安全地移除未被使用的调用从而获得更优的 Tree Shaking 效果。读完本文你将掌握该插件的安装方式、可标记的方法清单、底层源码工作原理、边界处理规则以及它在babel/preset-react预设中的实际应用场景。插件定位为 React 方法调用打上纯函数标记babel/plugin-transform-react-pure-annotations的核心使命正如其 README 所述Mark top-level React method calls as pure for tree shaking标记顶层 React 方法调用为纯函数以支持 Tree Shaking。在 JavaScript 生态中Tree Shaking 依赖编译器/打包器能够判断删除某段代码是否安全。默认情况下一个函数调用如React.createElement(div)可能产生副作用压缩器不敢贸然删除它。而/*#__PURE__*/是 Terser、esbuild、Rollup 等工具共同识别的标准注解一旦函数调用前出现该注解工具就假定该调用的返回值在未被使用时可安全删除前提是参数本身无副作用。该插件做的事情正是自动、精准地为确实无副作用的 React 顶层 API 调用批量注入这一注解替代开发者手工编写避免遗漏和误标。从 src/index.ts 的源码可以看到插件内部维护了一份纯方法白名单const PURE_CALLS: [string, Setstring][] [ [ react, new Set([ cloneElement, createContext, createElement, createFactory, createRef, forwardRef, isValidElement, memo, lazy, ]), ], [react-dom, new Set([createPortal])], ];即来自react的cloneElement、createContext、createElement、createFactory、createRef、forwardRef、isValidElement、memo、lazy以及来自react-dom的createPortal共 10 个顶层 API 会被标记。安装与基本用法README 给出了标准的包管理器安装方式。使用 npmnpm install --save-dev babel/plugin-transform-react-pure-annotations或使用 yarnyarn add babel/plugin-transform-react-pure-annotations --dev安装后在 Babel 配置中启用即可例如在babel.config.js中module.exports { plugins: [babel/plugin-transform-react-pure-annotations], };从仓库的 package.json 可以看出该包的工程约束它声明了babel/core为 peer 依赖^8.0.0运行时依赖babel/helper-annotate-as-pure与babel/helper-plugin-utils测试通过babel/helper-plugin-test-runner驱动engines字段要求 Node^22.18.0 || 24.11.0包类型为 ESMtype: module。实际使用时多数场景你不需要直接安装它——因为它默认被包含在babel/preset-react预设中启用该预设即自动生效。无需任何配置选项该插件不接收任何 options 参数行为完全由内置白名单决定。源码 src/index.ts 中插件主体仅声明了name与一个CallExpressionvisitorexport default declare(api { api.assertVersion(REQUIRED_VERSION(^7.0.0-0 || ^8.0.0)); return { name: transform-react-pure-annotations, visitor: { CallExpression(path) { if (isReactCall(path)) { annotateAsPure(path); } }, }, }; });api.assertVersion(^7.0.0-0 || ^8.0.0)表明该插件同时兼容 Babel 7 与 Babel 8。转换效果从普通调用到纯函数调用先看一个最典型的输入输出。测试夹具 test/fixtures/react/createElement/input.jsimport React from react; React.createElement(div);经过插件转换后见 output.mjsimport React from react; /*#__PURE__*/React.createElement(div);React.createElement(div)前被注入了/*#__PURE__*/注释。此后若该表达式的结果从未被使用Terser 等压缩器便可以在死代码消除阶段将其整行移除。同理test/fixtures/react/memo/input.js 中的React.memo((props) null)也会被标记为纯调用。再来看 ReactDOM 的夹具 test/fixtures/react-dom/createPortal/output.mjsimport * as React from react; import ReactDOM from react-dom; const Portal /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/React.createElement(div), document.getElementById(test));这里可以观察到两个细节其一ReactDOM.createPortal(...)被标记为纯调用其二嵌套在参数中的React.createElement(div)同样被标记。也就是说插件会递归访问 CallExpression 节点对每一个命中白名单的调用都做标记而非仅处理最外层。源码原理白名单匹配与去重保护匹配两类调用形态isReactCall见 src/index.ts是整个插件的核心判定逻辑它区分两种调用形态形态一具名导入的直接调用。当 callee 不是 MemberExpression 时判定该标识符是否通过referencesImport(module, method)引用了白名单模块的具名导出例如import { forwardRef } from react; forwardRef(...) // 命中形态二默认导入 / 命名空间导入的成员调用。当 callee 是 MemberExpression 且属性为非计算!callee.computed的普通 Identifier 时先检查object是否引用了模块的default或*导入再确认属性名是否在白名单内import React from react; // React.createElement(...) 命中 import * as React from react; // React.memo(...) 命中计算属性调用会被忽略一个值得注意的边界callee.computed为 true 时即Reactmethod这种计算属性访问插件不进行标记。测试夹具 test/fixtures/react/invalid-computed/input.js 与 output.mjs 证实了这一点——输入与输出完全一致未注入任何注解import React from react; var cloneElement, createElement; ReactcloneElement);原因很好理解计算属性名是运行时变量无法静态确认它指向的到底是哪一个 React API贸然标注纯函数可能导致压缩器删除本不该删除的代码因此插件采取保守策略。注解注入的幂等保护注解的写入由babel/helper-annotate-as-pure完成源码见 packages/babel-helper-annotate-as-pure/src/index.tsconst PURE_ANNOTATION #__PURE__; const isPureAnnotated ({ leadingComments }: Node): boolean !!leadingComments leadingComments.some(comment /[#]__PURE__/.test(comment.value)); export default function annotateAsPure(pathOrNode: Node | { node: Node }): void { const node (pathOrNode.node || pathOrNode) as Node; if (isPureAnnotated(node)) { return; } addComment(node, leading, PURE_ANNOTATION); }该 helper 通过babel/types的addComment在节点前追加#__PURE__前导注释同时具备幂等保护若节点已存在匹配[#]__PURE__的注释即同时兼容#__PURE__与__PURE__两种写法则跳过不重复添加避免与手工标注或上游转换重复叠加。测试体系基于夹具的全量校验插件的测试入口是 test/index.js通过babel/helper-plugin-test-runner的runner(import.meta.url)驱动属于 Babel 仓库标准的 fixture 测试模式每个用例目录下存放input.js、output.mjs与options.json三件套测试框架加载options.json中的插件配置对input.js执行转换并与output.mjs逐字比对。现有的测试夹具覆盖了react模块下的cloneElement、createContext、createElement、createFactory、createRef、forwardRef、isValidElement、lazy、memo、invalid-computed以及react-dom模块下的createPortal正好与白名单一一对应并额外验证了计算属性不标记这一边界行为。测试用例如 options.json 所示以sourceType: module配合插件名开启转换{ sourceType: module, plugins: [transform-react-pure-annotations] }典型应用场景配合压缩器获得更小的产物该插件的价值在仅使用 React 顶层 API 的一部分、且配合 Tree Shaking的项目中最为显著。例如一个项目只用到createElement而未使用memo、lazy但依赖链上存在import { memo } from react之类的未使用导入时标注为纯函数后Terser 可在压缩阶段安全移除这些调用与关联的不可达代码从而缩小最终 bundle 体积。一个值得说明的注意点README 中top-level顶层强调的是这些 API 调用在模块层面即可被判定为无副作用而非插件只处理字面意义上的顶层语句。从 visitor 的实现看只要是CallExpression且命中白名单无论位于函数体内还是嵌套表达式中都会被标记如前述createPortal夹具中嵌套的createElement。在生产实践中由于babel/preset-react默认包含该插件绝大多数 React 项目无需单独安装配置。若你的项目使用了自定义的 Babel 配置链、未引入 preset-react或希望明确了解该插件带来的转换可按照本文第一节的方式显式安装并启用它并通过 Babel 的输出检查/*#__PURE__*/注解是否按预期注入。小结babel/plugin-transform-react-pure-annotations是一个小而精的优化型 Babel 插件它通过维护一份权威的 React 纯 API 白名单react的 9 个方法与react-dom的createPortal在编译期自动为命中调用注入/*#__PURE__*/注解为 Terser 等压缩器的死代码消除提供安全依据从而放大 Tree Shaking 的收益。其源码结构清晰、零配置、幂等安全、对计算属性调用保守处理并配套完整的 fixture 测试体系是理解编译器如何帮助打包器做优化的一个极佳示例。【免费下载链接】babel Babel is a compiler for writing next generation JavaScript.项目地址: https://gitcode.com/gh_mirrors/ba/babel创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻