- 前端构建
- 构建工具
- 开发工具
【免费下载链接】farm
Extremely fast Vite-compatible web build tool written in Rust
本文面向使用 Farm 构建 Web 应用的开发者,围绕 语法降级和 Polyfill 一文的主题展开。Farm(用 Rust 编写的高速 Vite 兼容构建工具)在生产模式下可通过规范化选项output.targetEnv自动完成旧浏览器语法降级与 polyfill 注入,也可以分别通过compilation.presetEnv与compilation.script.target精细控制转换行为。读完本文,你将掌握带版本浏览器目标的选型方法、node_modules的 polyfill 策略、独立配置语法与 polyfill 的组合技巧,并理解其背后的 Rust 插件与配置归一化实现。
语法降级与 Polyfill 的定位
在现代前端工程中,源码往往使用了async/await、Promise、class、可选链、Symbol等新特性,而生产环境必须兼容 IE9 这类旧浏览器,甚至某些企业内网环境仍停留在 ES5。这就产生两个任务:
- 语法降级(Syntax Downgrade):将
Js/Jsx/Ts/Tsx模块中的 ES2015+ 语法转换为目标环境能理解的 ES5/ES2015 等价代码; - Polyfill 注入(Polyfill Injection):为缺失的运行时 API(如
Promise、regenerator-runtime、core-js的各类模块)补充实现。
Farm 的做法是:在生产模式下,当你选择带版本号的浏览器目标(如browser-es2017、browser-es2015或browser-legacy)时,自动启用匹配的语法降级与 polyfill 注入;而当你只需要控制某一部分时,也可以关闭这一整套预设,改为精确配置。
值得提前说明的默认行为是:默认情况下,Farm 不会对node_modules/下的模块进行转换和注入 polyfills。如果你需要为node_modules/中的包也做降级与 polyfill,可以使用compilation.presetEnv.include显式纳入(详见下文)。
配置output.targetEnv:一键选择兼容目标
Farm 提供规范化配置项output.targetEnv来声明应用的目标执行环境。带版本的浏览器目标会自动启用对应的「语法降级 + polyfill 注入」组合。例如,将应用编译为 ES5(兼容 IE9 等旧浏览器):
export default { compilation: { output: { targetEnv: 'browser-legacy', }, }, };设置browser-legacy后,Farm 会把项目编译为旧版浏览器(ES5)可运行的产物:
- 将所有的
Js/Jsx/Ts/Tsx模块编译到 ES5; - 注入所需的全部 polyfills(
Promise、regenerator-runtime等)。
支持的targetEnv值
Farm 支持一组规范化目标值,覆盖浏览器与 Node 两大运行场景:
| 目标值 | 语义 |
|---|---|
browser-esnext | 编译到最新现代浏览器,不注入任何 polyfill |
browser-es2017 | 编译到原生支持async/await的浏览器 |
browser-es2015 | 编译到原生支持 ES6 特性的浏览器 |
browser-legacy | 编译到 ES5(例如 IE9),会引入大量 polyfill,使产物体积显著增大 |
browser | 浏览器产物,不附带任何版本化降级预设 |
node16 | 编译到 Node 16 |
node-legacy | 编译到 Node 10 |
node-next | 编译到最新 Node 版本,不注入 polyfill |
node | Node 产物,不附带版本化降级预设 |
library | 以库模式构建(可配合format数组一次产出多种格式) |
其中browser是应用构建的默认值;当构建的是 npm 包(而非应用)时,应显式设置为library。详细说明见 compilation-options.md。
需要特定兼容性目标时请显式设置targetEnv,而不要依赖隐式推导——显式声明能让构建行为可预期,也便于团队统一兼容基线。
安装core-js的前提
由于 polyfill 注入依赖core-js与regenerator-runtime的运行时实现,你可能需要手动安装core-js@3或regenerator-runtime。如果遇到类似can not resolve 'core-js/modules/xxx'的报错,请运行:
pnpm add core-js内部机制:targetEnv如何联动presetEnv与script.target
从源码实现看,targetEnv并不是一个"魔法开关",而是在配置归一化阶段被展开为一系列具体配置。Farm 的 JS 侧配置归一化逻辑位于 normalize-output.ts,其中定义了一张目标映射表targetsMap:
browser-legacy→ 语法生成目标es5,浏览器目标为chrome >= 5, edge >= 12, firefox >= 4, ie >= 9, ios >= 6, opera >= 12.1, safari >= 5;browser-es2015→ 语法生成目标es2015,浏览器目标为「完整支持 Promise」的浏览器(chrome >= 33, edge >= 12, firefox >= 29, ios >= 8, opera >= 20, safari >= 7.1);browser-es2017→ 语法生成目标es2017,浏览器目标为「完整支持 async 函数」的浏览器(chrome >= 55, edge >= 15, firefox >= 52, ios >= 11, opera >= 42, safari >= 11);node16→scriptTargets: ['node 16'];node-legacy→scriptTargets: ['node 10'];node-next/browser-esnext/library无预设(null)。
在normalizeTargetEnv函数中,Farm 会根据上述映射自动做三件事:
- 若
presetEnv未被显式禁用(!== false),则把浏览器/Node 目标写入presetEnv.options.targets(用于 polyfill 的按需注入); - 若
script.target未显式设置,则写入语法生成目标scriptGenTarget(es5/es2015/es2017/esnext); - 若
css.prefixer未被显式禁用,则同步写入对应的 CSS 浏览器目标,实现 CSS 自动前缀与语法降级目标一致。
这就是为什么只配置一行targetEnv: 'browser-legacy'就能同时覆盖 JS 语法降级、polyfill 注入和 CSS 前缀的原因。同时,在 normalize-prod-default.ts 中resolvedCompilation.presetEnv ??= isProduction,即presetEnv的默认值是「开发模式false、构建模式true」,确保 polyfill 只在生产构建中生效。
底层实际执行转换的插件是 Rust 实现的FarmPluginPolyfill(lib.rs),编译阶段由 compiler/src/lib.rs 在config.preset_env.enabled()为真时注入插件列表。该插件通过 SWC 的swc_ecma_preset_env能力,对 AST 执行transform_from_env(按targets降级语法)与transform_from_es_version(按指定 ES 版本降级,用于 Farm 运行时模块),随后注入 helpers 并执行hygiene与fixer收尾,保证产物 AST 干净、变量无冲突。
分别配置语法与 Polyfill:compilation.presetEnv
targetEnv虽然方便,但粒度较粗。Farm 允许你关闭自动联动、分别精确配置presetEnv与script.target。
compilation.presetEnv负责「语法降级 + polyfill 注入」整体行为,类型可以是布尔值(true/false)或配置对象。其 TS 形态定义在 compilation-options.md:
type FarmPresetEnvConfig = | boolean | { include?: string[]; // 额外需要 polyfill 的模块(正则字符串数组) exclude?: string[]; // 不需要 polyfill 的模块(正则字符串数组) options?: any; // 传给 SWC preset-env 的配置,如 targets assumptions?: any; // SWC 转换假设(assumptions) };- 默认值:开发模式
false,构建模式true; presetEnv.include:默认[]。额外纳入需要 polyfill 的模块,配置正则字符串,例如include: ['node_modules/(es6-package|my-package)/'];presetEnv.exclude:默认['node_modules/']。配置不需要 polyfill 的模块。默认排除node_modules,如需纳入被排除的模块,推荐使用include而非改动exclude;presetEnv.options:默认「降级到 ES5」,即把targets等选项原样传给 SWC 的 preset-env(对应 SWC 文档中的env配置节)。
典型示例:为node_modules中的包注入 polyfill
export default { compilation: { presetEnv: { // 将 node_modules 下的某个包纳入 polyfill 范围 include: ['node_modules/package-name'], options: { targets: 'Chrome >= 48', }, }, }, };这里include支持正则字符串,例如'node_modules/(es6-package|my-package)/'可一次纳入多个包。
用宽松targets换取更小产物
如果项目并不需要太老的浏览器兼容,可以为targets设置一个较宽松的值(如'> 0.25%, not dead'或'Chrome >= 48')。presetEnv采用**按需注入(usage mode)**模式,targets越宽松,能被识别为"目标环境原生支持"的特性就越少注入 polyfill,产物体积自然更小。这一点在插件实现中得到印证:FarmPluginPolyfill::new在读取配置时强制mode = Some(Mode::Usage)(见 lib.rs),即只注入代码中实际使用到、且目标环境缺失的 polyfill,而非全量引入core-js。
关于 polyfill 运行时,插件默认设置了enforce_exclude: ['node_modules/core-js'],并在config阶段为@swc/helpers添加别名映射(指向runtime.swc_helpers_path),确保注入的 helper 能被正确解析。
配置compilation.script.target:只控制语法生成
script.target用于控制生成代码时的目标语法版本,即 AST 生成阶段支持的 ES 版本。可取值包括es5、es6、es2015~es2023、esnext,默认值为esnext(会随 Farm 迭代动态调整)。完整取值枚举可见 schema.ts 中的ECMAVersion,也参见 compilation-options.md。
需要注意的是:只降级语法而不注入 polyfill 是不够的。语法降级解决的是"语法能否被解析",而运行时 API(如Promise)缺失仍然会导致运行时报错。因此若要将项目完整降级到 ES5,需要同时设置:
export default { compilation: { script: { target: 'ES5', }, presetEnv: { // 将 node_modules 下的某个包纳入 polyfill 范围 include: ['node_modules/package-name'], options: { targets: '> 0.25%, not dead', }, }, }, };当只需要控制生成的 JS 语法、其余交给默认值时,也可以直接开启presetEnv:
import { defineConfig } from '@farmfe/core'; export default defineConfig({ compilation: { script: { target: 'es5', }, presetEnv: true, }, });这种「script.target+presetEnv」的组合也是 features/script.md 中推荐的旧浏览器降级方式。
决策建议:一键预设还是精细控制?
- 追求简单、团队统一兼容基线:直接设置带版本的
output.targetEnv(browser-es2017/browser-es2015/browser-legacy),Farm 会自动同步presetEnv.options.targets、script.target和 CSS 前缀目标; - 只关心 JS 语法版本、不需要 API polyfill:只配置
compilation.script.target; - 需要为
node_modules注入 polyfill:在presetEnv中通过include显式纳入,注意默认exclude: ['node_modules/']的存在; - 需要严格控制产物大小:为
presetEnv.options.targets设置较宽松的浏览器范围,减少按需注入的 polyfill 数量; - 无需旧浏览器兼容:使用
browser-esnext(或node-next),polyfill 注入被完全关闭,产物最小。
注意事项与常见问题
browser-legacy的体积代价:它会把所有脚本编译到 ES5 并注入大量 polyfill,使生产产物显著增大。仅在确实需要支持 IE9 等旧浏览器时选用,否则优先browser-es2017/browser-es2015。node_modules默认被排除:若某个依赖包含 ES2015+ 语法或缺失 API 而未做降级,构建或运行会异常,请在presetEnv.include中按正则纳入。core-js解析错误:遇到can not resolve 'core-js/modules/xxx'时,先pnpm add core-js安装core-js@3。presetEnv只在生产生效:开发模式下默认false,若想在开发模式验证降级效果,需显式配置presetEnv。script.target与presetEnv职责不同:前者只改语法生成,后者负责语法降级与 polyfill 注入,完整降级到 ES5 需两者配合。
结合 Rust 编译器的逐模块 AST 转换(FarmPluginPolyfill在插件链中以i32::MIN的优先级最后执行),Farm 将「语法降级 + 按需 polyfill」做成了生产构建中的一项自动化能力。理解targetEnv与presetEnv/script.target的联动关系后,你就可以在兼容范围与产物体积之间找到最合适的平衡点。
- 前端构建
- 构建工具
- 开发工具
【免费下载链接】farm
Extremely fast Vite-compatible web build tool written in Rust
相关推荐
Farm 语法降级与 Polyfill 注入完全指南:targetEnv、presetEnv 与 script.target 实战
Farm 语法降级与 Polyfill 注入完全指南:targetEnv、presetEnv 与 script.target 实战 本文以 Rust 构建工具
前端构建构建工具开发工具Farm 脚本编译配置完全指南:TS/TSX 解析、目标降级、Decorators 与 SWC 插件实战
Farm 脚本编译配置完全指南:TS/TSX 解析、目标降级、Decorators 与 SWC 插件实战 Farm(基于 Rust 的高性能 Vite 兼容构建
前端构建构建工具开发工具Akka.NET 中的 HOCON 配置语法与实战指南:从基础语法到源码级解析
Akka.NET 中的 HOCON 配置语法与实战指南:从基础语法到源码级解析 HOCON(Human Optimized Config Object Nota
后端微服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考