你有没有遇到过这种情况:明明只是打包一个简单的工具库,却要花大量时间配置 tsup、rollup 或 webpack,还要手动处理 .d.ts 声明文件、多格式输出、Tree Shaking……
这篇文章带你认识 tsdown —— 一个由 Rolldown 官方出品、比 tsup 快数倍的新一代 TypeScript 库打包工具。读完后你将能够:
✅ 理解 tsdown 的定位和核心优势
✅ 从零搭建一个完整的 TypeScript 库项目
✅ 掌握入口配置、多格式输出、声明文件生成等核心功能
✅ 用 Watch 模式提升开发体验
一、tsdown 是什么?为什么值得关注?
如果你写过 npm 包,大概率用过 tsup 或者 rollup。它们都能做到将 TypeScript 源码打包成可发布的 JS 文件,但有一个共同的痛点:随着项目变大,构建速度越来越慢。
tsdown 的出现,就是为了解决这个问题。
用一句话理解 tsdown
tsdown = tsup 的使用体验 + Rolldown(Rust 写的打包核心)的性能
就好比你以前骑自行车上班(tsup),现在换了一辆电动摩托(tsdown):路还是那条路,操作方式差不多,但速度快了好几倍。
tsdown 的核心优势
传统打包工具链:
TS 源码 → tsc 编译 → rollup 打包 → 输出 JS
↓ 慢 ↓ JS 实现,慢
tsdown 的链路:
TS 源码 → Oxc(Rust 实现)→ Rolldown(Rust 实现)→ 输出 JS
↓ 极快 ↓ 极快
四大核心特性:
| 特性 | 说明 |
|---|---|
| 🚀 极速构建 | 底层由 Rust 实现的 Oxc + Rolldown 驱动,远超 esbuild |
| ♻️ 生态兼容 | 兼容 Rollup 插件、Unplugin 插件、部分 Vite 插件 |
| 🛠️ 零配置 | 开箱即用,专为库作者设计,默认值合理 |
| 🔄 平滑迁移 | 兼容 tsup 的主要配置项,老项目迁移成本极低 |
值得一提的是:tsdown 是 Rolldown 的官方项目,Vite、Vitest、Nuxt、腾讯、Binance 等知名项目都在使用它。未来它还将成为 Rolldown Vite 的 Library Mode 基础。这意味着它不是玩具,而是有长期维护保障的生产级工具。
二、环境准备
在开始之前,确认你的开发环境满足以下要求:
# 推荐环境
Node.js >= 22.18.0 # 强烈推荐,低于此版本将在下个小版本移除支持
pnpm >= 9.0.0 # 本文使用 pnpm,也可替换为 npm/yarn/bun
# 检查 Node 版本
node -v
# 检查 pnpm 版本(未安装则执行下一行)
pnpm -v
npm install -g pnpm
注意 tsdown 要求 Node.js >= 20.19.0,推荐 >= 22.18.0。低于 20.19.0 将无法运行。 :::
三、10 分钟跑通第一个库项目
3.1 方式一:使用官方脚手架(推荐新手)
tsdown 提供了 create-tsdown 脚手架,一行命令生成完整项目:
pnpm create tsdown@latest
运行后,CLI 会交互式地询问你:
- 项目名称
- 是否使用 TypeScript(选 Yes)
- 目标平台(Node.js / Browser / 两者都要)
生成的项目结构如下:
my-library/
├── src/
│ └── index.ts # 你的库代码入口
├── tsdown.config.ts # tsdown 配置文件
├── tsconfig.json # TypeScript 配置
├── package.json
└── README.md
3.2 方式二:手动初始化(适合已有项目)
如果你想在现有项目中引入 tsdown,按以下步骤操作:
第一步:安装 tsdown
# 安装 tsdown 为开发依赖
pnpm add -D tsdown
# 如果不使用 isolatedDeclarations,还需要安装 TypeScript
pnpm add -D typescript
第二步:创建源码文件
mkdir src
创建 src/hello.ts:
// 导出一个简单的问候函数
export function hello(name: string): string {
return `Hello, ${name}! Welcome to tsdown.`
}
创建 src/index.ts(库的入口文件):
// 统一从这里导出所有公开 API
export { hello } from './hello.ts'
第三步:创建配置文件
在项目根目录创建 tsdown.config.ts:
import { defineConfig } from 'tsdown'
export default defineConfig({
entry: ['./src/index.ts'], // 指定入口文件
})
第四步:添加 npm scripts
在 package.json 中添加构建命令:
{
"name": "my-library",
"type": "module",
"main": "./dist/index.mjs",
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch"
},
"devDependencies": {
"tsdown": "^0.21.0"
}
}
第五步:运行构建
pnpm build
此时运行效果:终端显示构建成功信息,dist/ 目录下生成 index.mjs 文件。
# 验证输出是否正确
node dist/index.mjs
# 输出: Hello, World! Welcome to tsdown.
🎉 恭喜!你已经成功用 tsdown 打包了第一个 TypeScript 库!
四、核心配置详解
4.1 入口文件(Entry)
入口支持多种写法,满足不同场景:
import { defineConfig } from 'tsdown'
export default defineConfig({
// ✅ 写法一:单入口(字符串)
entry: './src/index.ts',
// ✅ 写法二:多入口(数组)
// entry: ['./src/index.ts', './src/cli.ts'],
// ✅ 写法三:带别名的多入口(对象)
// entry: {
// index: './src/index.ts', // 输出为 dist/index.mjs
// utils: './src/utils.ts', // 输出为 dist/utils.mjs
// },
})
4.2 输出格式(Format)
tsdown 默认输出 ESM 格式,但你可以根据需求配置多格式输出:
import { defineConfig } from 'tsdown'
export default defineConfig({
entry: './src/index.ts',
// 同时输出 ESM 和 CJS,兼容不同环境
format: ['esm', 'cjs'],
// 高级:不同格式使用不同编译目标
// format: {
// esm: { target: ['es2020'] },
// cjs: { target: ['node18'] },
// },
})
配置后的输出文件:
dist/
├── index.mjs # ESM 格式,供现代 JS 环境使用
└── index.cjs # CJS 格式,供 Node.js require() 使用
同时,在 package.json 中正确声明两种格式的入口:
{
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}
4.3 声明文件(.d.ts)
TypeScript 库必须提供 .d.ts 文件,让使用者能获得类型提示。tsdown 让这件事变得非常简单:
方式一:在 package.json 中声明 types 字段(自动启用)
{
"types": "./dist/index.d.ts"
}
只要 package.json 里有 types 或 typings 字段,tsdown 会自动启用 .d.ts 生成,什么配置都不需要写。
方式二:在配置文件中显式开启
import { defineConfig } from 'tsdown'
export default defineConfig({
entry: './src/index.ts',
dts: true, // 开启声明文件生成
})
:::tip 性能小技巧 在 tsconfig.json 中启用 isolatedDeclarations: true,tsdown 会使用 Rust 实现的 oxc-transform 来生成 .d.ts 文件,速度极快!
{
"compilerOptions": {
"isolatedDeclarations": true
}
}
:::
配置完成后,dist/ 输出如下:
dist/
├── index.mjs # JS 文件
├── index.cjs # CJS 格式
└── index.d.ts # TypeScript 类型声明
4.4 Watch 模式(开发神器)
开发阶段频繁修改代码时,Watch 模式会监听文件变化并自动重新构建:
# 命令行方式
tsdown --watch
# 或简写
tsdown -w
也可以在配置中默认开启(通常写在 dev script 里而非配置文件):
// 一般不推荐写在 config 里,用 npm script 控制更灵活
export default defineConfig({
entry: './src/index.ts',
// watch: true, // 不推荐写死在配置里
})
五、完整实战:打包一个真实工具库
下面我们做一个实际的例子:打包一个字符串工具库 string-utils,包含驼峰转换、截断、模板填充等功能,同时输出 ESM + CJS + .d.ts。
项目结构:
string-utils/
├── src/
│ ├── index.ts # 统一导出入口
│ ├── camelCase.ts # 驼峰转换
│ └── truncate.ts # 字符串截断
├── tsconfig.json
├── tsdown.config.ts
└── package.json
src/camelCase.ts
/**
* 将 kebab-case 或 snake_case 字符串转为 camelCase
* @example toCamelCase('hello-world') → 'helloWorld'
*/
export function toCamelCase(str: string): string {
return str
// 将连字符或下划线后的字母替换为大写
.replace(/[-_](.)/g, (_, char: string) => char.toUpperCase())
}
src/truncate.ts
/**
* 超出指定长度时截断字符串并追加省略号
* @example truncate('Hello World', 5) → 'Hello...'
*/
export function truncate(str: string, maxLength: number, suffix = '...'): string {
// 未超出长度,直接返回原字符串
if (str.length <= maxLength) return str
// 截断到最大长度,追加省略标记
return str.slice(0, maxLength) + suffix
}
src/index.ts
// 统一从这里导出所有公开函数
export { toCamelCase } from './camelCase.ts'
export { truncate } from './truncate.ts'
tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"isolatedDeclarations": true // 启用极速 dts 生成
},
"include": ["src"]
}
tsdown.config.ts
import { defineConfig } from 'tsdown'
export default defineConfig({
entry: './src/index.ts', // 入口文件
format: ['esm', 'cjs'], // 同时输出两种格式
dts: true, // 生成 TypeScript 声明文件
clean: true, // 每次构建前清空 dist 目录
sourcemap: true, // 生成 source map,便于调试
})
package.json
{
"name": "string-utils",
"version": "1.0.0",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "tsdown",
"dev": "tsdown --watch"
},
"devDependencies": {
"tsdown": "^0.21.0",
"typescript": "^5.5.0"
}
}
运行构建:
pnpm build
此时运行效果 —— dist/ 目录下生成如下文件:
dist/
├── index.mjs # ESM 格式
├── index.cjs # CJS 格式
├── index.d.ts # 类型声明
└── index.mjs.map # source map
验证输出是否正确:
# 创建一个临时测试文件
node -e "
import('./dist/index.mjs').then(({ toCamelCase, truncate }) => {
console.log(toCamelCase('hello-world')) // 输出: helloWorld
console.log(truncate('Hello World', 5)) // 输出: Hello...
})
"
六、常见问题(FAQ)
❓ 问:tsdown 和 tsup 的主要区别是什么?
💡 答: 两者定位相同(库打包工具),使用体验也很接近,主要区别在底层引擎:tsup 基于 esbuild(Go 实现),tsdown 基于 Rolldown(Rust 实现)+ Oxc,速度更快。此外 tsdown 是 Rolldown 官方出品,与未来的 Rolldown Vite 生态深度绑定。如果你从 tsup 迁移,大部分配置可以直接照搬,改动极小。
❓ 问:配置文件支持哪些格式?
💡 答: tsdown 支持 tsdown.config.ts、.js、.mjs、.cjs、.json 等,也可以直接在 package.json 的 tsdown 字段中写配置。推荐使用 .ts 格式,有完整的类型提示。
❓ 问:声明文件生成报错,怎么处理?
💡 答: tsdown 内部使用 rolldown-plugin-dts 来生成 .d.ts 文件。如果遇到报错,首先检查是否安装了 typescript(pnpm add -D typescript),然后确认 tsconfig.json 配置是否正确。若确认是插件 bug,去 rolldown-plugin-dts 提 issue。
❓ 问:如何输出 IIFE 格式供浏览器 <script> 标签直接使用?
💡 答:
export default defineConfig({
entry: './src/index.ts',
format: 'iife',
globalName: 'StringUtils', // 挂载到 window.StringUtils
})
构建后用 <script src="dist/index.global.js"></script> 引入即可。
七、总结与进阶路线
本文核心收获:
- tsdown = tsup 的体验 + Rolldown(Rust)的性能,适合所有 TypeScript 库项目
- 核心三步:安装 → 配置
tsdown.config.ts→pnpm build - 多格式输出:
format: ['esm', 'cjs'] - 声明文件:
package.json加types字段,或配置dts: true isolatedDeclarations: true可显著提升.d.ts生成速度