babel-preset-razzle 深度解析:Razzle 通用 JavaScript 应用的 Babel 预设配置指南
2026/9/24 14:58:27 网站建设 项目流程
  • 前端
  • 构建工具
  • 前端构建
  • 后端

【免费下载链接】razzle

✨ Create server-rendered universal JavaScript applications with no configuration

项目地址:https://gitcode.com/gh_mirrors/ra/razzle
点击查看免费下载

babel-preset-razzle是 Razzle 框架默认集成的 Babel 预设,它为服务端渲染(SSR)的通用 JavaScript 应用统一提供 JSX、TypeScript、现代 JavaScript 语法转换与运行时代码优化能力。本文以该预设的官方文档为主线,结合本仓库中预设的源码实现与 Razzle 内部调用链,完整讲解其核心配置、可选参数、在 Razzle 项目内外的接入方式,以及通过.babelrc扩展自定义转换的实战方案,帮助读者理解并驾驭 Razzle 的编译管线。

一、什么是 babel-preset-razzle

babel-preset-razzle是 Razzle 用于编译源码的 Babel 预设包,包内通过index.js导出一个标准的 Babel preset 函数。它的定位非常聚焦:无论应用代码使用 React、TypeScript 还是现代 ES 语法,都能基于NODE_ENV环境变量和 Babel 的 caller 元数据,自动产出一套适合"服务端 + 客户端"双构建目标的转换配置。

在仓库中,该包的入口文件为 packages/babel-preset-razzle/index.js,包内同时维护了 4 个自定义 Babel 插件(位于 packages/babel-preset-razzle/babel-plugins),用于处理 JSX pragma 注入、Hook 解构优化等 Razzle 特有的编译细节。

二、在 Razzle 项目中使用(默认内置)

官方 README 明确指出:在 Razzle 项目中最简单的使用方式就是直接使用 Razzle,因为它已经默认内置了该预设

从源码可以印证这一点:Razzle 核心包的依赖列表中声明了babel-preset-razzle(见 packages/razzle/package.json),其 Babel loader 在未发现用户自定义 Babel 配置时,会默认追加该预设(见 packages/razzle/config/babel-loader/razzle-babel-loader.js);Jest 测试转换器在没有.babelrc时同样默认解析该预设(见 packages/razzle/config/jest/babelTransform.js)。

因此,通过create-razzle-app创建的项目无需任何额外配置即可获得 ES6+ 语法、JSX、TypeScript 等编译能力。create-razzle-app 的默认模板也将babel-preset-razzle写入依赖(见 packages/create-razzle-app/templates/default/package.json),并在模板 README 中说明"自带你需要的 ES6 特性(通过 babel-preset-razzle)"。

三、在非 Razzle 项目中使用(独立接入)

如果要在不使用 Razzle 构建的项目中单独使用该预设,官方文档给出了两步操作:

第一步:安装 Babel。需要先具备@babel/core等基础环境。

第二步:在项目根目录创建.babelrc文件,内容如下:

{ "presets": ["razzle"] }

之后 Babel 便会通过名为razzle的预设完成转换。

提示:该预设要求目标运行环境具备Object.assign。文档明确说明:预设中的 @babel/plugin-proposal-object-rest-spread 插件使用了useBuiltIns选项,转换出的展开/剩余语法会直接调用Object.assign,因此必须保证Object.assign可用或被 polyfill(例如在旧浏览器上先加载对应的 polyfill)。

关于useBuiltIns: true的底层含义:它让对象展开语法编译为Object.assign({}, ...)而不是引入独立的辅助函数,从而避免为每个文件重复注入转换代码,有助于减小产物体积,但代价是把运行时依赖转移给了宿主环境。

四、预设的核心行为:环境感知与双目标编译

阅读 packages/babel-preset-razzle/index.js 的源码,可以还原该预设内部的工作机制。整体流程分为三层:环境判定 → caller 元数据读取 → 组装 presets 与 plugins

4.1 基于 NODE_ENV 判定构建模式

const env = process.env.NODE_ENV; const isProduction = env === 'production'; const isDevelopment = env === 'development'; const isTest = env === 'test';
  • production:开启 prop-types 移除、不注入 JSX 源码调试信息;
  • development:为@babel/preset-react开启development模式(自动引入 jsx-source / jsx-self 调试插件);
  • test:与 development 同样激活 React 开发模式,并让preset-envmodules保持自动判定,以适配 Jest 的 CommonJS 环境。

4.2 通过 caller 元数据区分服务端/浏览器/现代浏览器

预设通过api.caller()读取 Razzle loader 注入的三项关键元数据(注入逻辑见 razzle-babel-loader.js):

caller 字段含义对预设行为的影响
supportsStaticESM目标环境是否支持原生 ESM决定transform-runtime是否启用useESModules
isServer当前构建目标是否为 Node 服务端服务端/测试目标默认将preset-envtargets 设为node: 'current',且不注入transform-runtime,额外启用 BigInt 语法插件
isModern是否为现代浏览器构建结合preset-env.targets.esmodules共同决定isLaxModern,进而影响experimental-modern-preset的启用

其中,服务端/测试目标在没有显式指定 targets 时的默认处理代码如下:

if ( (isServer || isTest) && (!presetEnvConfig.targets || !( typeof presetEnvConfig.targets === 'object' && 'node' in presetEnvConfig.targets )) ) { presetEnvConfig.targets = { // Targets the current process' version of Node. This requires apps be // built and deployed on the same version of Node. node: 'current', }; }

值得注意的是注释中强调的限制:使用默认node: 'current'意味着应用必须在与构建环境相同版本的 Node 上构建和部署

4.3 三层预设的组合

预设最终输出sourceType: 'unambiguous',并按顺序组合以下 presets(见 index.js):

  1. @babel/preset-env:默认配置modules: 'auto'(生产/开发下由 webpack 自行处理 import/export 以支持 tree-shaking,测试环境自动转为 CommonJS),并排除transform-typeof-symbol;可通过options['preset-env']覆盖。
  2. @babel/preset-react:默认在 development/test 下开启development: true;当 React runtime 不是automatic时注入pragma: '__jsx';可通过options['preset-react']覆盖。
  3. @babel/preset-typescript:默认开启allowNamespacesallExtensionsisTSX;可通过options['preset-typescript']覆盖,设置preset-typescript: false可整体禁用。

此外,若满足isLaxModern(即isModern为真,或preset-env.targets.esmodules === true)且指定了options['experimental-modern-preset'],则用该自定义现代预设替换@babel/preset-env,这是为现代浏览器构建预留的实验性扩展点。

五、内置插件清单与作用

预设按需组装以下 plugins(见 index.js):

插件触发条件作用说明
babel-plugins/jsx-pragmaReact runtime 非 automatic为含 JSX 的模块自动注入import React from 'react'var __jsx = React.createElement,免除手动 import React
babel-plugins/optimize-hook-destructuring始终启用(lib: trueconst [state, setState] = useState(...)转为对象属性的按需解构,配合 tree-shaking 减小体积;lib: true表示仅优化来自 React / Preact 的 Hook(源码见 optimize-hook-destructuring.js)
@babel/plugin-syntax-dynamic-import始终启用支持动态import()语法
@babel/plugin-proposal-class-properties默认启用,可class-properties: false禁用支持 class 字段语法
@babel/plugin-proposal-object-rest-spread始终启用,useBuiltIns: true对象展开/剩余,使用内置Object.assign
@babel/plugin-transform-runtime仅非服务端构建corejs: falsehelpers: trueregenerator: true,并按supportsESM决定useESModules,复用@babel/runtime辅助函数
babel-plugin-transform-react-remove-prop-typesproduction移除 prop-types 声明,removeImport: true同时移除导入
@babel/plugin-proposal-optional-chaining始终启用可选链?.语法
@babel/plugin-proposal-nullish-coalescing-operator始终启用空值合并??语法
@babel/plugin-syntax-bigint仅服务端构建让 Node 端解析 BigInt 字面量
@babel/plugin-proposal-numeric-separator始终启用数字分隔符1_000_000

其中两个自定义插件值得展开说明:

jsx-pragma 插件(源码见 packages/babel-preset-razzle/babel-plugins/jsx-pragma.js):它会在 Program 节点退出时检查是否出现JSXElement/JSXFragment,若存在且作用域中没有可复用的React绑定,则自动在文件头部插入import React from 'react'var __jsx = React.createElement;。如果已有const React = require('react')这类 CommonJS 绑定,则会把变量声明插入到该 require 语句之后,保证执行顺序正确。

optimize-hook-destructuring 插件(源码见 packages/babel-preset-razzle/babel-plugins/optimize-hook-destructuring.js):默认通过/^use[A-Z]/匹配所有 Hook 风格的函数调用,将const [a, b] = useHook()改写为const { 0: a, 1: b } = useHook(),这样未被读取的索引不会被 webpack 打包,实现 Hook 结果的按需保留。设置onlyBuiltIns: true时仅匹配 React 内置 Hook。

六、在 Razzle 中扩展 Babel 配置

官方 README 与 create-razzle-app 模板(见 packages/create-razzle-app/templates/default/README.md)均说明:在项目根目录添加.babelrc即可扩展 Babel 转换。此时.babelrc会替换 Razzle 内部的默认 Babel 模板,因此至少必须包含默认的razzle/babel预设

{ "presets": [ "razzle/babel", // 必须保留 "stage-0" ], "plugins": [ // 其他自定义插件 ] }

这里的razzle/babel是 Razzle 包对外暴露的入口(见 packages/razzle/babel.js,其内容即module.exports = require('babel-preset-razzle')),等价于直接使用babel-preset-razzle

仓库中的示例examples/with-custom-babel-config演示了这一用法:其.babelrc保留了razzle/babel预设并追加了@babel/plugin-proposal-do-expressions插件(见 examples/with-custom-babel-config/.babelrc),随后在src/Home.js中直接使用了实验性的do表达式语法(见 examples/with-custom-babel-config/src/Home.js),并通过npx create-razzle-app --example with-custom-babel-config with-custom-babel-config创建、yarn start运行(见 examples/with-custom-babel-config/README.md)。

运行机制razzle-babel-loaderconfig阶段检查cfg.hasFilesystemConfig()(即是否存在.babelrc/babel.config.js),存在则打印"使用外部 Babel 配置"的日志并交由 Babel 自行读取,否则才把内置的 razzle 预设追加进去(见 packages/razzle/config/babel-loader/razzle-babel-loader.js)。

七、按构建目标拆分 Babel 配置(.babelrc.node / .babelrc.web)

Razzle 还支持为服务端(node)与浏览器(web)两个构建目标分别配置 Babel。启用方式是在razzle.config.js中设置enableTargetBabelrc: true(默认值为false,见 packages/razzle/config/defaultOptions.js):

// razzle.config.js module.exports = { options: { enableTargetBabelrc: true } };

启用后,Razzle 会在构建时把 Babel 的configFile指向项目根目录下的.babelrc.node(服务端目标)或.babelrc.web(浏览器目标),对应实现位于 packages/razzle/config/createConfigAsync.js:

configFile: razzleOptions.enableTargetBabelrc ? path.resolve(paths.appPath, `.babelrc.${target}`) : undefined,

仓库示例examples/with-custom-target-babel-config演示了该用法:它的 .babelrc.node 与 .babelrc.web 内容一致,均保留razzle/babel预设并追加@babel/plugin-proposal-do-expressions插件。这一机制非常适合"服务端用一套转换、浏览器端用另一套转换"的场景(例如只在某一端启用特定语法插件或按需加载 polyfill)。

八、源码级补充:Razzle 构建管线中的预设增强

除了预设本身,Razzle 的 loader 还会在编译时叠加若干增强,帮助理解预设在实际构建中的完整形态(见 packages/razzle/config/babel-loader/razzle-babel-loader.js):

  • React Fast Refresh 集成:当启用shouldUseReactRefresh时,会前置注入react-refresh/babel插件;客户端构建还会额外注入babel-preset-razzle/babel-plugins/no-anonymous-default-export,该插件会在遇到匿名默认导出函数时发出警告——因为匿名箭头函数/匿名函数声明会导致 Fast Refresh 无法保留组件本地状态(源码见 packages/babel-preset-razzle/babel-plugins/no-anonymous-default-export.js,警告信息由 loader 的onWarning回调输出)。
  • CommonJS 兼容:当源码出现module.exports且不是现代构建时,追加babel-preset-razzle/babel-plugins/commonjs插件,将含module.exports的文件转译为 CommonJS,避免 Babel 注入的import语句与 webpack 的模块解析冲突(见 packages/babel-preset-razzle/babel-plugins/commonjs.js)。
  • 环境变量内联:通过babel-plugin-transform-defineprocess.env.NODE_ENVtypeof windowprocess.browser等按目标构建时静态替换(见 razzle-babel-loader.js)。
  • 现代构建(isModern):loader 提供razzleBabelPresetModern包装函数,将preset-env的 targets 改为esmodules: true并排除transform-regeneratortransform-async-to-generator,实现面向现代浏览器的更小产物(见 razzle-babel-loader.js)。
  • Babel 缓存:loader 默认开启基于内容哈希的缓存目录(cache/razzle-babel-loader),缓存标识包含 server/modern/development 等维度,可通过enableBabelCache控制(见 razzle-babel-loader.js 与 defaultOptions.js)。

九、总结

babel-preset-razzle虽然只是一个预设包,但它集中体现了 Razzle"零配置 + 可逃生舱"的设计哲学:默认情况下,它为 Razzle 项目提供涵盖 React JSX、TypeScript、现代语法、运行时辅助与生产优化的完整编译能力;当项目需要定制时,既可以通过.babelrc追加插件,也可以借助razzle/babel入口独立于 Razzle 使用,甚至可以为 node 与 web 两个构建目标分别维护 Babel 配置。理解预设内部的环境判定、caller 元数据与插件组合逻辑,是在 Razzle 项目中排查编译问题、定制转换行为的必备基础。

参考文件索引

  • 预设入口源码
  • 预设包配置
  • 自定义 Babel 插件目录
  • Razzle 的 Babel loader 集成
  • Razzle 的 Jest Babel 转换器
  • Razzle 默认构建选项
  • 目标级 Babel 配置实现
  • 自定义 Babel 配置示例
  • 按目标拆分 Babel 配置示例
  • 前端
  • 构建工具
  • 前端构建
  • 后端

【免费下载链接】razzle

✨ Create server-rendered universal JavaScript applications with no configuration

项目地址:https://gitcode.com/gh_mirrors/ra/razzle
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询