Rspack Loader 系统的工作原理是什么?
Rspack 的 Loader 系统是其处理各类文件的核心机制。虽然 Rspack 基于 Rust 开发,但设计上充分考虑了与 Webpack Loader 生态的兼容性——大部分社区 Loader 可以直接使用,同时通过内置 Loader 提供了显著的性能优势。理解 Loader 系统的工作方式,是用好 Rspack 的关键前提。
Loader 的角色与定位
Loader 是一种模块转换器,负责将源文件转换为 Rspack 能够处理的模块格式。它的核心能力包括:
- 语言编译:将 TypeScript、JSX 等编译为 JavaScript
- 样式处理:将 Less、Sass 编译为 CSS
- 资源管理:处理图片、字体等静态资源
- 代码校验:集成 ESLint 等代码检查工具
Rspack 中 Loader 的定位与 Webpack 一致:先通过 Loader 对模块进行预处理,转换为 Rspack 支持的模块类型,再根据 rules[].type 进行后置处理。这两步构成了完整的模块处理流水线。
配置方式
基本规则
在 module.rules 中通过 test 匹配文件扩展名,用 use 指定 Loader:
javascriptmodule.exports = { module: { rules: [ { test: /\.js$/, use: 'babel-loader' } ] } }
多 Loader 链式调用
多个 Loader 组成链式处理,例如处理 CSS 文件:
javascriptmodule.exports = { module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader', 'postcss-loader'] } ] } }
带选项的 Loader
通过 options 传递配置参数:
javascriptmodule.exports = { module: { rules: [ { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } } ] } }
执行顺序:从右到左与 Pitch 机制
Loader 链的执行顺序是理解整个系统的关键。
基本执行顺序
Loader 按从右到左、从下到上的顺序执行。对于 use: ['style-loader', 'css-loader', 'sass-loader']:
sass-loader先执行,将 SCSS 编译为 CSScss-loader解析 CSS 中的@import和url()style-loader将 CSS 注入 DOM
Pitch 阶段
每个 Loader 除了正常的转换函数外,还可以定义一个 pitch 方法。Pitch 阶段从左到右执行,在所有正常阶段之前运行。当某个 Loader 的 pitch 方法返回了值,后续 Loader 的 pitch 和正常阶段都会被跳过,执行流程直接回溯到前一个 Loader。
对于 use: ['a-loader', 'b-loader', 'c-loader'],完整执行流程为:
shella-loader pitch → b-loader pitch → c-loader pitch → c-loader normal → b-loader normal → a-loader normal
如果 b-loader 的 pitch 返回了值,则跳过 c-loader 和 b-loader 的 normal 阶段,直接进入 a-loader 的 normal 阶段。这个机制在 style-loader 的实现中被使用——它在 pitch 阶段拦截请求,直接返回一段将 CSS 注入页面的代码。
内置 Loader:Rspack 的性能利器
Rspack 提供了多个 Rust 实现的内置 Loader,在保持与 JavaScript Loader 相同可组合性的同时,提供了远超 JS Loader 的性能。
builtin:swc-loader
基于 SWC 的超快 JavaScript/TypeScript 编译器,替代 babel-loader 和 ts-loader:
javascriptmodule.exports = { module: { rules: [ { test: /\.(js|jsx|ts|tsx)$/, use: { loader: 'builtin:swc-loader', options: { jsc: { parser: { syntax: 'typescript', tsx: true }, transform: { react: { runtime: 'automatic' } } } } } } ] } }
builtin:swc-loader 的核心优势在于 SWC 本身就是 Rust 编写的,因此这个 Loader 运行在 Rspack 的 Rust 进程内部,无需跨语言通信开销。
builtin:lightningcss-loader
基于 Lightning CSS 的内置 CSS 处理器,可替代 postcss-loader + autoprefixer 的组合:
javascriptimport { rspack } from '@rspack/core'; module.exports = { module: { rules: [ { test: /\.css$/, use: [ { loader: 'builtin:lightningcss-loader', options: { targets: ['chrome 60', 'firefox 60'] } } ] } ] } }
主要配置项:
- targets:指定目标浏览器,支持 browserslist 查询字符串或版本号对象
- errorRecovery:默认
true,遇到无效 CSS 语法时跳过并发出警告而非中断编译
需要注意 Lightning CSS 严格遵循 CSS 规范,处理非标准 CSS(如 CSS Modules 的局部作用域语法)时可能出现兼容问题。
builtin:css-loader
Rspack 还提供了 builtin:css-loader 作为 css-loader 的内置替代:
javascriptmodule.exports = { module: { rules: [ { test: /\.css$/, use: ['style-loader', 'builtin:css-loader'] } ] } }
原生 CSS 支持
除了内置 Loader,Rspack 还通过 experiments.css 提供原生 CSS 支持,CSS 作为一等公民被直接处理:
javascriptmodule.exports = { experiments: { css: true } }
启用后可通过 type: 'css'、type: 'css/auto'、type: 'css/global'、type: 'css/module' 等模块类型控制 CSS 处理方式。此时不需要 css-loader,也不能与 style-loader 搭配使用,需使用 CssExtractRspackPlugin 或 Rspack 内置的 CSS 提取方案。
匹配规则与条件
test / include / exclude
- test:正则匹配文件路径
- include:限定只处理指定目录
- exclude:排除指定目录
javascript{ test: /\.js$/, include: path.resolve(__dirname, 'src'), exclude: /node_modules/, use: 'babel-loader' }
oneOf
当同一类文件只需匹配一个规则时,使用 oneOf 避免重复处理:
javascript{ test: /\.css$/, oneOf: [ { resourceQuery: /module/, use: 'css-loader?modules' }, { use: 'css-loader' } ] }
resource 与 issuer
- resource:匹配被导入的资源路径
- issuer:匹配发起导入的模块路径
javascript{ test: /\.css$/, issuer: /\.js$/, // 只处理从 JS 文件中导入的 CSS use: 'style-loader' }
自定义 Loader
同步 Loader
最简单的 Loader 形式,接收源码字符串,返回转换结果:
javascriptmodule.exports = function(content) { return content.toUpperCase(); };
异步 Loader
需要执行异步操作时,调用 this.async() 获取回调函数:
javascriptmodule.exports = function(content) { const callback = this.async(); someAsyncOperation(content).then(result => { callback(null, result); }).catch(err => { callback(err); }); };
Raw Loader
默认情况下 Loader 接收 UTF-8 字符串。当需要处理二进制文件时,导出 raw: true 以接收 Buffer:
javascriptmodule.exports = function(content) { // content 是 Buffer const size = content.length; return `export default ${size}`; }; module.exports.raw = true;
获取选项
通过 this.getOptions() 获取配置参数:
javascriptmodule.exports = function(content, map, meta) { const options = this.getOptions(); const result = content.replace(options.search, options.replace); return result; };
Rspack Loader 架构:与 Webpack 的关键区别
Rspack 的 Loader 系统在设计上与 Webpack 保持了 API 兼容,但底层架构有本质区别。
旧架构:Rust 端的 Loader Runner
在早期版本中,Rspack 将 JS Loader 转换为可以从 Rust 端调用的原生函数,Loader Runner 完全运行在 Rust 端。这种方式的局限在于对复杂 JS Loader 的兼容性不够理想。
新架构:标识符驱动的调度机制
当前架构中,Rspack 从 Rust 核心将 Loader 请求委托给 JS 端的调度器,使用修改版的 webpack loader-runner 执行。每个 JS Loader 通过标识符(identifier)唯一识别,包含无法序列化字段的选项会复用 Loader 标识来避免重复解析。
Rust-JS 通信优化
由于 Rspack 的核心构建逻辑在 Rust 端,而社区 Loader 运行在 JS 端,两者之间的通信是性能瓶颈。Rspack 的优化策略是将连续的 JS Loader 组合在一起执行,减少 Rust-JS 之间的通信轮次。当模块处理链中包含多个 JS Loader 时,Rspack 会把它们合并为一次 JS 调用,而非每个 Loader 都进行一次跨语言通信。
增量构建优势
与 esbuild 的 onLoad 回调不同,Rspack 在 rebuild 时只触发变动模块的 Loader 重复执行,未变化的模块直接使用缓存。在大项目中这是一个关键的 O(n) 复杂度优势。
性能优化建议
- 优先使用内置 Loader:
builtin:swc-loader替代babel-loader,builtin:lightningcss-loader替代postcss-loader,性能提升通常在数倍到数十倍 - 缩小 Loader 作用范围:使用
include和exclude避免对node_modules等目录执行不必要的转换 - 启用缓存:对
babel-loader等仍需使用的 JS Loader 开启cacheDirectory - 使用原生 CSS 方案:如果项目不依赖
css-loader的特定功能,启用experiments.css可以获得更好的 CSS 处理性能 - 避免不必要的 Loader:Rspack 原生支持 Asset Modules,不需要
file-loader和url-loader,通过type: 'asset/resource'和type: 'asset'替代
兼容性说明
Rspack 支持绝大多数社区 Loader,只有少数依赖 Webpack 内部实现细节的 Loader(如 cache-loader)尚未兼容。如果遇到不兼容的情况,可以通过 Rspack GitHub Issues 反馈。