Lynx × Rspack 2.0:更快、更小、更贴近 Web

所有文章
2026年6月30日
李一鸣
李一鸣工程师 @ Lynx
卢恒昌
卢恒昌工程师 @ Lynx
王清雨
王清雨工程师 @ Lynx
胡静
胡静工程师 @ Lynx
赵靖凯
赵靖凯框架主管 @ Lynx
陈嘉涵
陈嘉涵Rstack 团队
黄玄
黄玄架构师 @ Lynx

Lynx 基于 Rstack(RsbuildRspack)构建了自己的工具链 Rspeedy,让每一个 Lynx 应用都能同步获得上游工具链的性能与能力。

随着 Rspack 2.0Rsbuild 2.0 相继发布,Lynx 免费获得了上游的一系列优化:构建提速 18~39%产物体积下降 3~9%,以及向 Web 通行约定对齐的配置,如 resolve.alias 和顶层 splitChunks。在此之上,我们针对 Lynx 做了一系列定制:更灵活的分包与动态加载面向 PrimJS 的产物调优默认 ES2017Lynx Bundle 并行编码,以及精确错误反解。我们还引入了一个用于包体积优化的 Agent Skill,帮你系统化地优化应用体积。

这些能力从 Rspeedy 0.15 起可用。我们预计绝大多数项目无需改动配置即可获得这些收益,需要迁移的部分见下文升级指南

Rspack 2.0

Rspack 是最底层的打包引擎。这次升级到 2.0,带来了最直接的性能与体积收益,绝大多数项目无需改动配置即可获得。Rspack 2.0 本身的完整变更,可参考 Rspack 2.0 发布博客了解。

构建提速 18~39%

在一组生产环境的 Lynx 应用上,我们测得构建耗时下降 18~39%,且不需要任何配置改动:

项目(已脱敏)升级前版本升级前升级后构建提速
App A0.9.1119.1s11.6s−39%
App B0.9.1022.7s14.2s−37%
App C0.9.1040.8s28.2s−31%
App D0.11.315.3s11.2s−27%
App E0.11.333.3s27.2s−18%

核心提速来自 Rspack 2.0 的引擎优化;多页应用还会进一步受益于 Lynx Bundle 的并行编码

产物体积下降 3~9%

升级后产物体积也随之下降。在中间产物 main-thread.jsbackground.js 与最终的 Lynx Bundle 中,main-thread.js 与 Lynx Bundle 普遍下降 3~9%。

项目(已脱敏)background.jsmain-thread.jsLynx Bundle
App A0.47 → 0.48MB(+1.3%)0.46 → 0.43MB(−6.7%)1.14 → 1.10MB(−3.1%)
App B0.82 → 0.77MB(−6.0%)0.93 → 0.84MB(−9.5%)2.13 → 1.95MB(−8.5%)
App C2.05 → 1.98MB(−3.6%)2.16 → 2.01MB(−7.0%)5.03 → 4.74MB(−5.8%)
App D0.30 → 0.30MB(−1.1%)0.51 → 0.47MB(−7.2%)0.82 → 0.79MB(−3.3%)
App E0.49 → 0.50MB(+1.4%)2.98 → 2.79MB(−6.5%)4.63 → 4.48MB(−3.1%)
以上为 DEBUG 模式下中间产物的全页面汇总体积,升级前 → 升级后。

这部分收益来自 Rspack 2.0 更强的 tree-shaking,以及我们把后台产物默认目标提升至 ES2017

Node.js 版本要求提升

最低要求提升至 Node.js 20.19+ 或 22.12+,不再支持 Node.js 18。

@lynx-js/*-webpack-plugin 不再支持 webpack

既然 Rspack 已经足够成熟,我们也将底层 @lynx-js/*-webpack-plugin 系列插件转为 Rspack-only:公开类型统一从 @rspack/core 导出。绝大多数项目通过 Rspeedy 间接使用,无需改动;如果你直接依赖这些插件,需要相应调整。

Rsbuild 2.0

Rsbuild 在 Rspack 之上提供开箱即用的构建配置。2.0 让这些配置进一步向 Web 生态的通行约定靠拢;这一层的变更主要集中在配置项的命名与位置调整上,语义保持不变。完整变更可参考 Rsbuild 2.0 发布博客

source.aliasresolve.alias

将别名从 source.alias 迁移到 resolve.alias

export default defineConfig({
-  source: {
+  resolve: {
    alias: {
      '@': './src',
    },
  },
})

performance.chunkSplitsplitChunks

performance.chunkSplit.strategy 迁移到顶层 splitChunks.preset

export default defineConfig({
-  performance: {
-    chunkSplit: {
-      strategy: 'single-vendor',
-    },
-  },
+  splitChunks: {
+    preset: 'single-vendor',
+  },
})

默认装饰器版本 2023-11

source.decorators.version 的默认值由 2022-03 变更为 2023-11,如需保持旧行为请显式配置。

面向 Lynx 的定制优化

在 Rspack 与 Rsbuild 之上,Rspeedy 做了一系列针对 Lynx 双线程运行时与产物格式的优化。

更灵活的分包与动态加载

Lynx 提供了由弱到强的三种拆分手段,它们在 Web 生态中都有对应的概念,并由 Rspeedy 适配到了 Lynx 的产物形态上:

场景Web 生态Lynx
构建期拆包Chunk Split(splitChunkssplitChunks(同名配置,提供 Lynx 适配的 preset)
应用内按需加载动态 import() + React.lazy动态 import() + ReactLynx lazy(),产物为 Lazy Bundle
外部 / 跨应用复用webpack externalsExternal Bundle

构建期拆包的 splitChunks 较为常见(配置迁移见 performance.chunkSplitsplitChunks),这里不再展开;下面重点说说 Lazy Bundle 和 External Bundle。

Lazy Bundle

Lazy Bundle(懒加载分包)把单个应用的一部分延后加载,用 ReactLynx 的 lazy() 按需拉取,体验与 Web 中的动态 import() 一致:

import { Suspense, lazy } from '@lynx-js/react';

const LazyComponent = lazy(() => import('./LazyComponent.jsx'));

在此基础上,我们正在为 Lazy Bundle 增加按 import 控制加载时机的能力:借助 Rspack 2.0 的 import attribute 支持,为每个 import 单独指定同步加载(用于首帧直出 IFR)或异步加载(后台驱动,默认)。届时的用法大致如下:

lazy(() => import('./LazyComponent.jsx', { with: { mode: 'sync' } })); // 同步,用于首帧直出
lazy(() => import('./LazyComponent.jsx', { with: { mode: 'async' } })); // 后台驱动(默认)

该能力正在开发中,将在后续版本提供。

External Bundle

External Bundle 是一个单独构建出来的 Lynx Bundle,可以被另一个应用在运行时拉取并渲染,从而实现跨应用复用。生产侧基于 Rslib 构建,用 defineExternalBundleRslibConfig 产出;消费侧则使用 Rsbuild 插件 pluginExternalBundle 接入。

面向 PrimJS:let / const 降级为 var

除了构建期更快,我们也让产物在 Lynx 的运行时里解析得更快。在 Android 上,Lynx 的后台线程运行在 PrimJS 引擎上,而我们发现 PrimJS 解析 var 的速度明显优于块级作用域的 let / const。因此 Rspeedy 0.15 默认在主线程与后台线程两层都开启了 SWC 的 transform-block-scoping,把 let / const 降级为 var,并将 output.environment.const 设为 false,让打包器自身生成的运行时代码也使用 var

默认目标提升至 ES2017

我们把后台线程的产物目标从 ES2015 提升到了 ES2017(主线程保持 ES2019)。随着低端机型逐渐退场,更高的产物基线意味着更少的语法降级、更小更快的产物。如果你仍需要降级某些特定语法,可以把对应的 transform 加到 tools.swcenv.include

import { defineConfig } from '@lynx-js/rspeedy';

export default defineConfig({
  tools: {
    swc: {
      env: {
        include: ['transform-async-to-generator'],
      },
    },
  },
});

Lynx Bundle 并行编码

对多页应用,构建提速不止来自 Rspack 2.0:Rspeedy 在一个并行的 worker 池中运行各 entry 的 tasm encode(@lynx-js/tasm),把原本串行的 Lynx Bundle 编码并行化,进一步缩短构建耗时。

精确的错误反解

Rspeedy 0.15 让线上错误能精确反解回源码:为每个 Lynx entry 生成统一的 debug-metadata.json,整合 JS、CSS、UI 三类 Source Map,主线程与后台线程的报错都能找到正确的反解信息、还原到原始代码。具体做法见线上错误反解

One More Thing:包体积优化 Agent Skill

准确定位体积瓶颈,往往比优化本身更难,这需要理解 Lynx 的产物格式和 Rspeedy 的双线程构建机制。rspeedy-bundle-size 把这套知识封装成了一个 Agent Skill:它让 AI 理解 Lynx 双线程产物的体积构成,按「先测量、后优化」的方式工作:先量清楚一个应用的体积大在哪,再按回报高低排序,优先处理收益最大的部分(资源、后台 JS、主线程产物中的冗余代码)。

Lynx 双线程产物的体积构成

把它安装到你的 Agent(Claude Code、Codex 等)中:

npx skills add lynx-community/skills -s rspeedy-bundle-size

之后用自然语言描述目标即可,例如「帮我拆解下这个应用的包体积,到底大在哪、怎么减」。Skill 会运行 Rsdoctor、按层给出体积拆解,并产出一张带优先级的优化清单,只有在你确认后才动手改代码。在真实业务中,这套工作流把页面体积减小了 10~13%,主要手段是把本应只在后台运行、却被打进了主线程的后台专属(background only)代码(如日志、网络请求、监控埋点等)正确标记并移出主线程,并通过 alias 去除重复打包的依赖。

升级指南

完整迁移步骤见 Rspeedy 升级指南。上文按层列出的破坏性变更,多数随 Rspack 2.0 / Rsbuild 2.0 升级引入。如需逐条核对,可参阅 Rspeedy CHANGELOGRsbuild v1 → v2 升级指南 以及 Rspack v1 → v2 迁移指南

接下来

我们会继续和 Rstack 团队一起优化构建性能与产物性能,同时让更多 Web 上已经成熟的能力可以在 Lynx 上开箱即用,例如 Module Federation。

你可能也注意到了上文 Rspack → Rsbuild → Rspeedy 的三层结构有些绕,我们也有同感。接下来我们会探索如何消除 Rspeedy 这一层:把双线程模型等 Lynx 特性沉淀到上游 Rstack,减少 Lynx 专属的配置、概念与文档,让长远来看你可以直接用 Rsbuild 配置和构建 Lynx 项目。

在你现有的项目上升级试试看吧。如果遇到问题或有建议,欢迎在 GitHub 上提 Issue 或参与讨论。

除非另有说明,本项目采用知识共享署名 4.0 国际许可协议进行许可,代码示例采用 Apache License 2.0 许可协议进行许可。