我前阵子帮一个团队评估新项目的构建方案。需求并不复杂:TypeScript 写的后台管理系统,有组件库、工具函数,还要把几个模块抽出来单独发布。问到 Webpack 几乎是本能的,毕竟团队所有存量项目都基于它,文档熟练,踩坑经验也足够。但聊得越深,我越清楚一件事:真正让人想换掉 Webpack 的,往往不是它“不能用”,而是它的配置成本、启动成本和心智负担。这个议题不算新鲜,但今年情况确实不太一样——TypeScript 已经接近默认基础设施,Vite 覆盖了大量应用开发场景,tsup 把库构建简化到近乎普通脚本,Rolldown 又把“Vite 底层换核”从话题变成了可尝试的路径。
所以我更愿意把这次思考描述成一个判断:这一轮工具更替,重点不是“打包更快”,而是把之前被 Webpack 硬塞进一个配置文件里的多件事拆开了。Webpack 像一个什么都管的大管家,能力边界宽,但任何一件事想改顺手,都要先理解那套相当庞杂的规则。而 tsup、Vite、Rolldown 的思路更接近分工协作:开发时用 Vite 的即时反馈,发布库时用 tsup 的干净产物,底层打包交给 Rolldown 或 esbuild 这类更高效的引擎。“告别 Webpack”不一定意味着否定它,而是意味着你的构建流程,不再由一个庞大的配置系统统一接管。
1. 先别急着告别:Webpack 教会了我们什么,又在哪里透支了体验
1.1 Webpack 当年解决的核心问题
回到 Webpack 盛行的时代,前端工程化面临的问题和今天完全不同。那时模块规范还在过渡,浏览器对 ES Module 的支持能力有限,开发者需要一套机制把散落各处的 JS、CSS、图片、字体统一处理。Webpack 的价值不只是“打包”,而是提供了一个统一资源处理模型:loader 负责把各种资源转成可打包的模块,plugin 负责在打包生命周期里做优化、注入、拆分。这个模型最厉害的地方在于,它把不确定的外部资源世界,收敛成一个可编程的构建流程。
直到今天,很多团队仍然依赖 Webpack 的 loader 体系和插件生态来解决特殊需求。比如样式预处理、图片压缩、代码混淆、产物注释清理、按需加载、多页面打包、复杂的环境变量注入。这些能力是真实的,也是很多项目正常运行的基础。任何一种替代方案在宣传自己“更快”之前,都得先回答一个问题:你的生态能不能覆盖这些实际场景?
从历史作用来说,Webpack 完成了前端工程化的一次大统一。正因为它太成功,后来才有大量项目建立在对它的路径配置、loader 顺序、plugin 实现细节的依赖之上。理解了这一层,你才能理解“告别 Webpack”为什么不能只是一次简单的工具替换。
1.2 想更换工具的三种真实信号
不是所有项目都需要换工具。我见过不少团队,把 Webpack 配置优化得很好,构建速度也在可接受范围,那确实没必要为了追新而追新。但如果你遇到下面三种信号,就该认真考虑新的组合方案了。
第一种信号:配置增量已经超过业务增量。一个新页面不需要写几行业务代码,反而要先搞清楚“为什么这个 svg 走不了”、“为什么新增的目录没被打进产物”、“为什么 node_modules 里的某个包又被编译了一遍”。这种时候,工具已经从基础设施变成了日常阻力。
第二种信号:dev server 的启动和热更新时间开始影响开发节奏。Webpack 的缓存策略和持久化缓存配置可以解决一部分问题,但它的架构决定了,全量编译和复杂依赖图的处理开销很难被彻底消掉。当改动一个文件要等 5 到 10 秒甚至更久,开发状态就会被打断。很多人对 Webpack 的抱怨不是“打包慢”,而是“等待感太强”。
第三种信号:你希望同一份代码既能作为应用的一部分运行,又能被独立发布成库或组件给其他项目使用。Webpack 虽然也支持 library 模式,但配置起来并不轻松,还要处理 externals、UMD、ESM 产物兼容等问题。相比之下,用 tsup 这类面向库构建的工具,几乎可以在一份不到十行的配置里完成同样的事情。
1.3 一个容易被忽略的干扰项:有些“ts”和 TypeScript 无关
搜索“TS”这个关键词时,经常看到两类完全不同的内容:一类是 TypeScript,另一类是视频传输流中的分片文件。比如“下载视频变成了很多 ts 文件”、“ffmpeg ts 伪装 jpg”、“带 subtitle 的 ts 流”这些,和前端工程化没有任何关系。它们只是恰好用了同一个缩写。
我提这件事,是因为工具选型时最怕被错误信息干扰。如果你发现自己绕进“TS 不是 TypeScript”的检索迷宫里,先退出来,把自己要找的东西定义清楚。这篇文章里的 TS,全部指 TypeScript。
想替换 Webpack 不是情绪问题,而是判断问题。你要先看清当前项目到底被什么困住,再决定新方案承担什么角色。
2. 组合拳不是堆工具:tsup、Vite、Rolldown 如何分工
2.1 三层分工模型:应用构建、库构建、打包内核
很多人一开始容易做错一件事:把 tsup、Vite、Rolldown 当成“三个 Webpack 替代品”来回对比,然后试图选一个赢家。实际上,这三者在当前生态里不是同一个层级的东西,它们解决的问题也不一样。
一个相对清晰的分层方式是:
- 应用层:你用 Vite 启动开发服务器、代理接口、编译应用代码、产出 web 应用所需的静态资源。
- 库层:你用 tsup 打包可复用代码,比如组件库、工具函数、请求封装、类型定义,输出 ESM 和 CJS 双格式,同时生成声明文件。
- 底层内核:Rolldown 或 esbuild 负责真正执行打包、转译和代码转换的算法。
Vite 是一个完整的应用开发与构建工具,它在开发阶段用 esbuild 做依赖预构建,在生产构建阶段用了 Rollup。tsup 则是一个更聚焦的“库打包器”,它的底层早期依赖 esbuild,后来也能接入别的底层能力。Rolldown 更偏向底层,它想用 Rust 重新实现一个和 Rollup API 兼容的打包内核,未来可能替代 Vite 底层依赖的 Rollup。
用生活里的例子类比:Vite 像一套精装房的施工流程,从进场到交付都管;tsup 像一个家具工厂,负责把某些标准模块做成可搬走的成品;Rolldown 更像生产流水线上的核心机床,效率更高,但它本身不直接面向最终用户。
这套分工模型想传递的核心观点是:工具组合的价值不在工具数量多,而在于每个工具只承担自己最擅长的一层。你不需要把库构建和应用构建的所有配置都堆在同一个工具里。
2.2 为什么会出现 Rolldown 这种“底层换核”方案
要理解 Rolldown,得先理解 Vite 现在的一个隐性摩擦。Vite 在开发阶段使用 esbuild 做依赖预构建和 TS 转译,速度非常快;但在生产构建阶段使用 Rollup,因为 esbuild 的产物控制和代码分割能力还达不到完整替代 Rollup 的程度。这带来一个客观问题:开发环境和生产环境的模块处理逻辑并不完全一致。很多“开发环境正常,打包后报错”的诡异问题,根源就在双引擎差异。
Rolldown 的思路是用 Rust 重写一个 Rollup 兼容的打包内核,它既保留 Rollup 的插件生态和产物模型,又能获得接近 esbuild 的性能。如果这个方向成熟,Vite 的开发和生产构建可以共用同一套内核,从而消除偏差。这也就是“只要换一个底层,整个上层体验都会变化”的逻辑。
需要强调,Rolldown 不是基于 esbuild 的简单替代,也不是 Vite 的替代方案。它是给 Vite 这类构建工具提供更优底层的实验性项目。从公开进展看,它已经有部分里程碑版本发布,但距离“所有生产项目无感切换”仍有距离。落地前务必以官方文档和最新 changelog 为准。
2.3 这套组合适合谁,不适合谁
先说适合的场景。
如果你是新手项目、中小型 Web 应用,或者团队正在做技术栈统一、需要同时维护应用和多个库,那么 tsup + Vite 的组合非常值得尝试。它配置量小、反馈快、默认行为清晰,很容易把一个项目的构建流程控制在可理解的范围内。对于组件库、工具包、接口层封装这类产物,tsup 几乎是当前最省心的选择之一。
再说不适合的场景。
如果你有一个大型存量 Webpack 工程,里面用了大量自定义 loader、复杂 plugin、动态 require、依赖内部模块路径等写法,那么“告别 Webpack”的成本会非常高。这种情况下,更合理的策略不是推翻重来,而是渐进拆分:先把可以被标准工具处理的模块抽离出去,再逐步把剩余部分迁移到新工具上。
如果你只是被之前某个项目里的 Webpack 配置搞烦了,但新项目并不需要多库发布、组件复用和快速迭代,那组合拳的优势就不明显。工具是为你服务的,不是用来证明你技术很新的装饰品。
3. 落地基线:TS + Vite + tsup 的最小工程怎么搭
3.1 项目结构和依赖准备
我先给一个常见的最小结构。这里以“应用 + 独立库”并存的单仓库简化版为例展开,避免一上来引入复杂 monorepo 概念。
your-project/ ├─ src/ │ ├─ components/ │ ├─ pages/ │ ├─ utils/ │ ├─ api/ │ └─ index.ts ├─ package.json ├─ tsconfig.json ├─ tsup.config.ts └─ vite.config.ts依赖准备上,核心的大类如下:
- TypeScript:提供类型系统和 tsc 命令。
- Vite 以及对应框架插件,比如 Vue 项目用
@vitejs/plugin-vue,React 项目用@vitejs/plugin-react。 - tsup:负责库构建。
- 项目自己的业务依赖和开发依赖,建议放在 package.json 中明确区分。
安装方式很常规,直接使用 npm、pnpm 或 yarn 按项目规范安装即可。需要注意 Node 版本和包管理器版本,不同工具对运行环境的支持范围不同。如果原始材料没有给出明确版本,落地前要先确认依赖版本,不要拿旧版本硬套新配置。
3.2 tsconfig:先做类型检查,再做打包
类型检查是 TypeScript 项目的地基。很多人在 Vite 项目中把类型检查完全交给 Vite,这是不对的。Vite 用 esbuild 转译 TS 时只做语法转译,不会做完整的类型检查。也就是说,只要语法合法,即使类型有问题也能跑起来。
所以 tsconfig 至少要包含以下关键点:
{ "compilerOptions": { "target": "ES2020", "module": "ESNext", "moduleResolution": "Bundler", "strict": true, "skipLibCheck": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "isolatedModules": true, "declaration": true, "baseUrl": ".", "paths": { "@/*": ["src/*"] }, "outDir": "dist" }, "include": ["src"] }几个容易踩坑的点:
moduleResolution设为Bundler是因为 Vite、tsup 这类打包器使用 ESM 解析逻辑,它比Node更贴近实际运行环境。isolatedModules启用后,要求每个文件都能被单独转译,这比较符合 esbuild 的单文件转译模式。strict建议一直开着,它会在编译阶段拦截大量低级错误。
至于热词里经常出现的TS interface、TS partial、TS 封装 axios 设置响应返回类型,其实都指向同一个观点:类型定义是团队的约定文档,而不是装饰。一个项目想要长期维护,首先要让npm run typecheck成为提交代码之前必跑的检查项。
3.3 tsup 配置:库构建优先
tsup 的魅力在于默认配置足够聪明,你需要关心的字段不多。下面是一个常见的库构建配置:
import { defineConfig } from 'tsup' export default defineConfig({ entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: true, sourcemap: true, clean: true, splitting: false, treeshake: true, external: ['vue', 'react'], })逐项解释一下:
entry:库入口文件,通常是你希望对外暴露的公共 API。format:输出 ESM 和 CJS 两种格式。这样既支持现代构建工具,也能兼容老式 Node 项目。dts:生成.d.ts类型声明文件。这一步对库发布来说很重要,没有类型声明,使用方在 TS 项目里会非常痛苦。sourcemap:生成 sourcemap,方便使用方调试库代码。clean:每次构建前清空输出目录,避免旧文件残留。splitting:库构建时一般关闭代码分割,避免产物碎片化。external:把 vue、react 这类框架依赖标记为外部依赖,不打包进产物,由使用方项目自己提供。
注意:
external不是在配置里随便列两个包名就完事。你要先想清楚,哪些依赖应该打进产物,哪些应该交给使用方。打包进产物会让库体积变大,也容易造成多实例问题;不打包则会要求使用方必须安装对应依赖。
在具体项目里,如果你还负责工具函数或接口封装,可以给每个包单独维护一个 tsup 配置,甚至用多入口一次构建多个产物。但不要一上来就把所有模块塞进同一个入口,先保持入口稳定,再逐步扩展。
3.4 Vite 配置:应用构建的核心
Vite 配置更偏应用侧。以一个 Vue 项目为例,常见配置可以长这样:
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import { fileURLToPath, URL } from 'node:url' export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } }, server: { port: 5173, proxy: { '/api': { target: 'http://localhost:3000', changeOrigin: true } } }, build: { outDir: 'dist', sourcemap: true } })这里要重点理解两个部分。
resolve.alias决定了代码里的@/components/xxx指向哪里。它必须和 tsconfig 里的paths保持一致。如果 tsconfig 里配了@/*,但 Vite 里没配,开发时也许能通过,但类型检查和构建时可能就找不到模块。保持两边一致,是减少配置冲突的关键。
server.proxy解决了开发阶段的接口跨域和代理问题。它把前端请求转发到后端服务,从而让浏览器认为所有请求都来自同一个源。具体到某个/api/form/list请求失败时,往往不是 Vite 配置本身的问题,而是 target 指向的服务不可用、路径没有正确 rewrite、或者后端 CORS 策略太严格。
3.5 从单命令到开发、构建、类型检查、产出的闭环
配置好之后,你需要的不是一个命令,而是一组命令,形成闭环:
# 类型检查 npm run typecheck # 开发环境 npm run dev # 应用构建 npm run build # 库构建 npm run build:lib如果项目把类型检查放在npm run build之前,那 Vite 的打包就不会被类型错误悄悄带过。一个更稳的流程是:
- 先跑 typecheck,捕获类型问题。
- 再跑单元测试或 lint,保证逻辑和规范。
- 然后分别跑 build 和 build:lib,验证应用产物和库产物都正常。
- 最后检查 dist 目录里的文件名、sourcemap、声明文件是否齐全。
这一步看起来简单,但它决定了你的构建体系是“能跑”还是“可维护”。
4. Rolldown 的正确打开方式:实验性内核怎么理解、怎么试
4.1 为什么需要 Rust 重写打包内核
Rolldown 出现之前,前端社区对构建性能的追求已经分成了两条路线:一条是 esbuild 的“尽可能快”,另一条是 Rollup 的“尽可能符合生态”。esbuild 很快,但插件模型和产物控制比 Rollup 弱;Rollup 生态成熟,但 JavaScript 实现的性能在大型项目上有瓶颈。
Rolldown 想同时拿到两者的优点:用 Rust 提供高性能,同时保持 Rollup 兼容的 API,让大量现有插件不需要重写就能继续使用。这个方向一旦成立,Vite 的双引擎问题就会从根上解决。更重要的是,它把“构建工具”这件事从配置竞争提高到了“内核性能”竞争的层面。
4.2 在 Vite 中切换 Rolldown 的试验路径
因为 Rolldown 仍处于快速迭代阶段,不同版本入口和启用方式可能都不一样,所以我不在这里写死命令。更稳妥的做法是,把它当作一个实验分支来评估。
一个通用试验路径:
- 复制一份当前项目,或者新建一个空项目,不要直接改生产分支。
- 查看 Vite 和 Rolldown 官方文档、发布说明,确认当前版本推荐的启用方式。
- 在最小范围内验证插件兼容性。先跑通最简单的页面,再逐步加组件库、路由、状态管理、接口代理。
- 对比开发启动时间、热更新时间、生产构建时间和产物大小,记录数据。
在真实评估过程中,你会很快发现哪些插件和 Rolldown 有兼容问题,哪些构建行为发生了变化。这类信息比看任何评测文章都有价值。
注意:不要因为 Rolldown 性能数据亮眼,就直接把它引入重要生产环境。它更像是一辆性能很强的工程样车,适合在测试赛道里充分验证,不适合第一天就开着它上高速。
4.3 用 Rolldown 之前的边界检查
如果你想在项目里引入 Rolldown,建议先回答这几个问题:
- 你的项目版本和依赖版本是否支持当前 Rolldown 的启用方式?
- 你依赖的 Vite 插件是否都兼容 Rolldown 的插件模型?
- 项目的开发构建和生产构建是否都跑过一遍完整验证?
- 有没有做性能对比和产物差异对比?
- 万一出现严重问题,回退到 Rollup 或 esbuild 的路径是否顺畅?
如果有一个问题回答不了,就不要把它推向生产。用工具不是为了追踪最新版本,而是为了获得稳定可控的交付流程。
4.4 现在不建议做的几件事
以目前的成熟度,我不建议做下面几件事:
不建议把 Rolldown 直接作为默认工具写进新项目模板,除非你的团队愿意持续处理版本变化。不建议为了兼容 Rolldown 去改造现有插件链,这等于把迁移成本提前支付给了还不稳定的底层。不建议只因为 Rolldown 性能高就忽略构建结果差异,性能不是衡量构建工具的唯一指标。
这一章并不是否定 Rolldown,而是强调边界。工具越靠近底层,越需要谨慎评估。
5. 从 Webpack 迁移的实操路线:先抽层,再接管,最后换核
5.1 第一步:把可复用代码用 tsup 抽成独立包
从 Webpack 项目迁到新组合,最大的风险是“一刀切”。正确做法是先抽层。
先找出项目里可以被独立复用的部分:工具函数、通用组件、类型定义、请求封装。把它们放到独立目录或独立包中,用 tsup 打包。这样带来的第一个好处是,这些代码不再被应用构建捆绑,可以单独测试、单独发布。第二个好处是,后续应用迁移 Vite 时,不需要处理这部分复杂依赖。
这个阶段最容易遇到的问题是多包之间的依赖关系。如果 A 包依赖 B 包,并且通过相对路径直接引用 B 包源码,那再好的工具方案都会变得很脆弱。建议先明确包的对外入口,让包之间只通过入口文件依赖。
5.2 第二步:用 Vite 接管应用开发服务器和构建
抽离出可复用代码后,下一步是让应用开发从 Webpack 切到 Vite。
这一步不用追求一步到位。可以先把开发服务器切到 Vite,保留旧的 Webpack 构建作为发布构建,用一段时间对比差异。开发阶段主要关注:热更新是否正常、接口代理是否一致、环境变量是否完整、静态资源是否符合预期。如果开发体验稳定,再逐步把生产构建也切换过去。
这里要特别注意环境变量的替换规则。Webpack 里可能用process.env.NODE_ENV或自定义插件注入变量,Vite 则默认通过import.meta.env暴露变量,并用loadEnv读取.env文件。迁移时需要把老的环境变量格式梳理一遍,避免构建后出现 undefined。
5.3 第三步:评估 Rolldown 替换时机
当前两步都稳定之后,再考虑 Rolldown。
评估的关键时刻是:你的 Vite 插件生态已经稳定,构建产物确认没有异常,团队对 Vite 的配置和维护已经有足够经验。此时引入 Rolldown,主要目的是解决你确实遇到的构建瓶颈,而不是为了追新。没有明确瓶颈,就保持现状。
Rolldown 的替换时机不是由技术热度决定的,而是由稳定性和收益共同决定的。如果现有方案已经能让你把时间和精力放在业务上,那它就是一个好方案。
5.4 一份迁移检查清单
迁移到新构建体系之前,建议把这张检查清单过一遍。下面这些项是我在多个项目里都会确认的内容:
| 检查项 | 需要确认的问题 |
|---|---|
| 依赖版本 | Node 版本、包管理器版本、Vite/tsup/Rolldown 版本是否兼容 |
| 入口与出口 | 应用入口、库入口、输出目录和文件名是否清晰 |
| 环境变量 | 所有process.env和.env变量是否迁移到import.meta.env或统一配置 |
| 静态资源 | public 目录内容是否完整,资源路径是否发生变化 |
| 代理规则 | 开发代理是否与后端接口地址匹配,是否启用了 rewrite |
| 插件替代 | Webpack loader 和 plugin 是否找到 Vite/Rolldown 对应方案 |
| 产物清理 | 是否清空旧 dist,避免旧文件与新文件混在一起 |
| 类型声明 | 库产物是否生成 d.ts,应用类型检查是否独立执行 |
| 构建缓存 | CI 和本地构建缓存是否清掉,避免旧缓存干扰 |
| 回归验证 | 关键页面、接口、鉴权、路由是否全部走一遍 |
还有一个容易忽略的细节:Webpack 时代很多团队会配置注释清除或代码压缩相关的优化项,比如terser的format.comments、optimization.minimizer。切到 Vite 后,注释清理和压缩策略是由 Vite 自身或其生成的底层配置决定的,方法和 Webpack 完全不同。迁移时要主动检查产物里是否保留了不该保留的注释、签名或调试信息,不能默认“新工具一定会做”。
注意:迁移过程中,尽量把“工具替换”和“业务重构”分开。不要一边换构建工具,一边改业务模块结构。两件事同时发生时,你很难判断问题来自工具还是来自业务改造。
6. 高频故障排查链路:三个真实报错,一层一层查
6.1 问题一:Cannot find package 'vite'
我看到这个报错出现在很多新手项目里。现象是运行npm run dev时,Node 提示找不到 vite 包,有时还带着imported from的路径。
先别急着重装依赖。排查顺序应该是:
- 打开 package.json,确认 vite 是否真的出现在 devDependencies 或 dependencies 里。
- 检查当前目录位置,确认你是在项目根目录运行的命令,而不是子目录。
- 检查 node_modules 里是否存在 vite 目录。如果存在,检查版本是否与 package.json 声明一致。
- 如果使用 pnpm,还要确认是否因为工作区 hoisting 导致依赖被安装到了根目录,而子项目访问不到。
- 确认没有 lockfile 错乱后,再决定是否删除 node_modules 和 lockfile 重新安装。
最忌讳的是直接运行npm install vite,它可能把 vite 装到不正确的层级,或者安装了错误版本,反而把问题弄复杂。依赖问题要先看“依赖应该在哪里”,再看“实际在哪里”,最后才动手调整。
6.2 问题二:[vite] http proxy error
这个报错在开发环境里很常见。比如/api/form/list?page=1&pagesize=10请求代理失败。看到http proxy error时,首先要意识到,它通常不是 Vite 配置写错,而是代理链路里某个环节没通。
按下面几步排查:
- 先确认代理目标服务是否在运行。可以直接用 curl 请求
http://localhost:3000/api/form/list,看后端有没有响应。 - 检查 vite.config.ts 里的
proxy.target是否正确,端口是否写错。 - 检查是否需要 rewrite 路径。如果后端接口不带
/api前缀,就需要把前缀去掉。 - 检查
changeOrigin是否开启,尤其是后端服务对 Host 头有校验时。 - 检查本地是否使用了
localhost,而代理目标是 IPv6 地址或特定域名,解析可能存在差异。 - 可以开启构建工具的调试日志,观察代理请求实际发到了哪里。
这类问题影响面很大,但定位路径其实很固定:先确认目标服务,再确认代理规则,最后才怀疑工具本身。
6.3 问题三:public 目录与旧构建配置冲突
有一个常见提示是content not from webpack is served from public,这是 Webpack dev server 对 public 目录内容的说明。如果你在新项目里仍然看到类似信息,说明你可能还跑在旧构建体系下,或者旧构建脚本没有真正被替换。
另一个常被忽略的问题是,一个 dist 目录同时被 Webpack 和 Vite 写入。旧脚本还在跑构建,新脚本也在写产物,最后目录里的文件新旧混杂,很容易出现“新代码没生效”的错觉。
遇到这种情况,先关闭所有构建进程,清空 dist 目录,再确认当前 dev 和 build 命令实际执行的是哪个配置文件。检查 package.json 里的 scripts,检查有没有残余的 webpack.config.js 或老启动脚本。public 目录本身的处理逻辑在 Vite 里比较清晰:所有放在 public 下的文件都会原样复制到构建输出根目录,并且放在 public 之外的静态资源通过 import 或 URL 引用。迁移时要注意路径变化,尤其是 favicon、page 模板这类非 JS 资源。
6.4 通用排查顺序表
把上面的经验收拢成一张通用排查顺序表,以后遇到“构建工具行为异常”时,可以按这个顺序走:
| 阶段 | 要做的检查 | 典型问题 |
|---|---|---|
| 现象 | 到底报什么错、卡在哪里、输出什么 | 报错信息被忽略,直接猜配置 |
| 输入 | 入口文件、路径、编码、格式是否正常 | 文件路径大小写、文件损坏、编码不一致 |
| 环境 | Node、包管理器、依赖位置、版本 | 依赖安装层级错误、锁文件错乱 |
| 配置 | 入口、出口、别名、代理、环境变量 | 别名不一致、代理 target 错、环境变量缺失 |
| 资源 | public、静态文件、构建缓存 | 旧资源残留、缓存干扰 |
| 工具边界 | 插件兼容、已知缺陷、版本限制 | 插件不支持新版本、实验性功能不稳定 |
多数“诡异”问题,都能在输入或环境环节找到原因。别一上来就怀疑工具能力不够。
回到最初那个团队的问题,我的建议其实很简单:先在一个独立分支或新项目里把 tsup + Vite 的组合跑通,再思考是不是真要引入 Rolldown。如果你发现开发链路变短了、构建行为变清晰了、产物结构变稳定了,那这次工具更新就算成功。真正值得长期关注的不是“用上了某个新打包器”,而是你是否有能力控制构建流程的每一步。工具总在迭代,但“先跑通、再分流、再优化”的处理方法,才是这一类方案留给你最持久的东西。