为什么我放弃了 npm,投入 Bun 的怀抱
作为一个常年跟 JavaScript 打交道的开发者,我对 npm install 的速度已经麻木了。每次安装依赖,我都得去泡杯茶,回来可能还在转圈圈。直到有一天,我在 GitHub 上看到了 Bun 的介绍:"比 npm 快 30 倍"------这不就是我梦寐以求的工具吗?
抱着试试看的心态,我用 bun init -y 创建了一个新项目。结果让我震惊:从命令执行到项目初始化完成,前后不到 200 毫秒。那一刻,我仿佛看到了新世界的大门。
bash
$ bun init -y
bun init v1.3.14 (285c09e8)
✓ Created package.json
✓ Created tsconfig.json
✓ Created .gitignore
✓ Created README.md
✓ Created index.ts
Done! You can now run:
bun run index.ts
没有任何多余的提问,没有漫长的等待,一切都那么丝滑。这就是我决定把新项目都迁移到 Bun 的原因------效率就是生产力。
几行代码搞定 LLM API 调用
项目创建好了,接下来要实现核心功能:调用 DeepSeek 的大模型 API。这看似复杂,其实用 axios 几行代码就能搞定。
typescript
import axios from "axios";
async function chat() {
try {
const res = await axios.post(
`${process.env.DEEPSEEK_BASE_URL}/chat/completions`,
{
model: "deepseek-v4-flash",
messages: [{
role: "user",
content: "你好,介绍一下 bun"
}]
},
{
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.DEEPSEEK_API_KEY}`
}
}
);
console.log(res.data.choices[0].message.content);
} catch (err: any) {
console.error("请求失败:", err.message);
}
}
chat();
让我拆解一下这段代码的精妙之处:
HTTP 请求的三层结构
一个完整的 HTTP 请求就像一封书信:
- 请求行 :
POST /chat/completions------ 告诉服务器要做什么 - 请求头 :
Content-Type和Authorization------ 信封上的寄件人信息 - 请求体:模型参数和消息内容 ------ 信件正文
async/await 的魔法
async/await 是 JavaScript 史上最伟大的发明之一。它让异步代码看起来像同步代码:
await axios.post(...)会暂停执行,直到请求完成- 不需要嵌套的回调函数(告别"回调地狱")
- 错误处理用熟悉的 try-catch 语法
axios 的优势
为什么选择 axios 而不是原生的 fetch?
| 特性 | axios | fetch |
|---|---|---|
| 自动 JSON 解析 | ✅ | ❌ 需要手动 .json() |
| 拦截器支持 | ✅ | ❌ |
| 请求取消 | ✅ | ❌ |
| 超时处理 | ✅ | 需手动实现 |
| 浏览器兼容性 | ✅ | 现代浏览器 |
环境变量管理的正确姿势
API 密钥这种敏感信息,绝对不能硬编码在代码里!这是每个开发者都应该知道的基本原则。
.env 文件的秘密
项目根目录下的 .env 文件是存放敏感配置的最佳位置:
env
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
DEEPSEEK_API_KEY=sk-your-secret-key-here
然后在代码中通过 process.env.xxx 访问:
typescript
const baseUrl = process.env.DEEPSEEK_BASE_URL;
const apiKey = process.env.DEEPSEEK_API_KEY;
为什么不用 dotenv?
你可能会疑惑,为什么代码里没有 require('dotenv').config()?
因为 Bun 内置支持 .env 文件 !当你用 bun run 执行脚本时,它会自动加载项目根目录下的 .env 文件,无需任何额外配置。这又是 Bun 超越 Node.js 的一个细节。
.gitignore 的重要性
一定要确保 .env 文件在 .gitignore 中:
gitignore
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
想象一下,如果把 API 密钥提交到 GitHub,后果不堪设想------你的账户可能被恶意使用,产生巨额费用。
TypeScript 配置的最佳实践
项目中的 tsconfig.json 配置非常讲究,让我逐行解读:
json
{
"compilerOptions": {
"lib": ["ESNext"],
"target": "ESNext",
"module": "Preserve",
"jsx": "react-jsx",
"allowJs": true,
"types": ["bun"],
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true
}
}
关键配置解析
"lib": ["ESNext"]------ 启用最新的 ES 特性,告别 polyfill"module": "Preserve"------ 保持模块语法不变,让 Bun 处理打包"types": ["bun"]------ 添加 Bun 的类型定义,获得更好的 IDE 支持"moduleResolution": "bundler"------ 使用打包器风格的模块解析"allowImportingTsExtensions": true------ 可以直接 import.ts文件"strict": true------ 开启所有严格类型检查,提前发现潜在问题
为什么这些配置很重要
良好的 TypeScript 配置能带来:
- 更好的代码提示:IDE 能智能推断类型
- 更早的错误发现:编译时就能发现类型错误
- 更好的团队协作:统一的代码规范
从开发到生产:项目结构优化建议
当前项目结构非常简单,但如果要投入生产环境,还需要做一些优化。
当前结构
bash
axios-demo/
├── .gitignore
├── .env
├── README.md
├── bun.lock
├── index.ts
├── package.json
└── tsconfig.json
生产级结构建议
csharp
axios-demo/
├── src/
│ ├── api/
│ │ └── llm.ts # LLM API 封装
│ ├── config/
│ │ └── index.ts # 配置管理
│ └── index.ts # 入口文件
├── .env.example # 环境变量模板
├── .gitignore
├── README.md
├── bun.lock
├── package.json
└── tsconfig.json
API 封装示例
把 API 调用封装成独立模块:
typescript
// src/api/llm.ts
import axios from "axios";
const client = axios.create({
baseURL: process.env.DEEPSEEK_BASE_URL,
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.DEEPSEEK_API_KEY}`
},
timeout: 30000
});
export async function chatWithAI(message: string): Promise<string> {
const response = await client.post("/chat/completions", {
model: "deepseek-v4-flash",
messages: [{ role: "user", content: message }]
});
return response.data.choices[0].message.content;
}
这样做的好处:
- 代码复用:多个地方调用只需 import
- 易于测试:可以轻松 mock
- 统一配置:集中管理超时、拦截器等
坑点总结与避坑指南
在开发过程中,我遇到了几个值得分享的坑:
坑一:Bun 与 dotenv 的冲突
刚开始我同时使用了 dotenv 包和 Bun 的内置 .env 支持,结果出现了 injected env (0) 的提示------环境变量没有被正确加载。
解决方案 :删除 dotenv 依赖和相关代码,完全信任 Bun 的内置能力。
坑二:API 地址错误
一开始我把 API 地址写成了 https://api.deepseek.cn/v1,结果一直报 ECONNREFUSED。后来改成 https://api.deepseek.com/v1 就正常了。
解决方案:仔细核对官方文档的 API 地址。
坑三:async 函数忘记调用
写好 chat() 函数后,忘记在文件末尾调用,结果运行后什么都没发生。
解决方案:养成在文件末尾调用主函数的习惯,或者使用 IIFE:
typescript
(async () => {
await chat();
})();
坑四:环境变量未定义
如果 .env 文件不存在或变量名拼写错误,process.env.xxx 会返回 undefined,导致请求失败。
解决方案:添加环境变量检查:
typescript
if (!process.env.DEEPSEEK_BASE_URL || !process.env.DEEPSEEK_API_KEY) {
console.error("请配置环境变量");
process.exit(1);
}
结语
这个看似简单的项目,其实包含了很多现代 JavaScript 开发的最佳实践:
- 使用 Bun 替代 npm:提升开发效率
- axios 处理 HTTP 请求:简洁优雅
- 环境变量管理:安全第一
- TypeScript 严格模式:提前发现错误
- async/await 语法:异步代码同步化
技术的本质是解决问题,而好的工具能让我们更专注于解决问题本身。Bun + axios 的组合,就是这样一个让开发变得更轻松的工具链。
如果你也想体验飞一般的开发速度,不妨试试这个项目模板,然后告诉我你的感受!