🔥 前端直连大模型?别慌!Vite + Fetch 教你优雅地“套娃”API,顺便把 Key 藏好!
摘要:还在为前端调用 LLM 头疼?担心 API Key 裸奔被黑产扫号?本文手把手带你用 Vite 搭建全栈脚手架,利用 Fetch 发起复杂请求,并用
.env环境变量给 Key 穿上“隐身衣”。拒绝硬编码,从你我做起!🚫🔑
😱 前言:当“Hello World”遇上“API Key 泄露”
兄弟们,大模型(LLM)火得一塌糊涂。你也想在自己的网页里接个 DeepSeek 或者 ChatGPT,让用户跟 AI 聊聊天。
于是你兴冲冲地写了这段代码:
// ❌ 危险操作!千万别这么干!
const apiKey = "sk-1234567890abcdef..."; // 直接写死在代码里
fetch('https://api.deepseek.com...', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
然后你把代码推到了 GitHub。 三分钟后:你的邮箱炸了,信用卡账单多了几笔奇怪的消费,矿机正在你的 Key 上疯狂运转。💸
为什么? 因为前端代码是公开的!任何人在浏览器按个 F12,你的 Key 就像没穿衣服一样暴露在阳光下。
别怕!今天咱们就用 Vite 和 环境变量,给这个 Key 穿上一件“防弹衣”,顺便把 Fetch 复杂请求讲得明明白白。
🛠️ 第一步:工程化起手式 —— Vite 脚手架
别再用原生 HTML/CSS/JS 手动创建文件了,那是石器时代的做法。咱们要用 Vite,快如闪电,配置简单。
1. 初始化项目
打开终端,敲下这行咒语:
npm create vite@latest my-llm-app -- --template vanilla
# 或者你想用 TS/React/Vue 也可以,这里为了演示原理,我们用原生 JS (vanilla)
cd my-llm-app
npm install
2. 配置 .env —— Key 的保险箱
在项目根目录下(和 package.json 同级),创建一个名为 .env 的文件。
⚠️ 重点来了(敲黑板):
Vite 有一个特殊规定:只有以 VITE_ 开头的环境变量才会被暴露给客户端代码。 这是为了防止你不小心把数据库密码也传给了前端。
.env 文件内容:
# .env
# 这里的 VITE_ 前缀绝对不能少!
VITE_DEEPSEEK_API_KEY=sk-你的真实-Key-在这里
🔒 安全小贴士:
赶紧把 .env 加到 .gitignore 里!
echo ".env" >> .gitignore
这样,就算你手滑 push 了代码,Key 也不会上传到 GitHub。
🚀 第二步:Fetch 复杂请求 —— 像侦探一样拆解 HTTP
前端调用 LLM,本质上就是发一个 HTTP POST 请求。咱们把请求拆成三部分,像剥洋葱一样看清楚。
1. 请求行 (Request Line)
告诉服务器:我要干嘛?去哪?用什么协议?
- Method:
POST(因为我们要发送数据) - URL:
https://api.deepseek.com/chat/completions(注意必须是 HTTPS,http 会被浏览器拦截) - Version:
HTTP/1.1(浏览器自动处理,不用管)
2. 请求头 (Headers) —— 身份证和通行证
Content-Type: application/json: 告诉服务器,“我发给你的是 JSON 格式的文本,别解析错了”。Authorization: Bearer <Token>: 这是关键!格式固定为Bearer空格加上你的 Key。
3. 请求体 (Body) —— 真正的干货
浏览器不能直接发送 JS 对象 { role: 'user'... },必须序列化成字符串。
- 工具:
JSON.stringify(payload)
💻 第三步:代码实战 —— 异步函数的优雅舞步
来,看看如何在 main.js (或你的入口文件) 中写出既健壮又优雅的代码。
// src/main.js (或者 main.ts)
// 🔴 1. 获取 Endpoint (必须是 HTTPS)
const endpoint = 'https://api.deepseek.com/chat/completions';
// 🔴 2. 读取环境变量
// Vite 通过 import.meta.env 访问 .env 中以 VITE_ 开头的变量
const apiKey = import.meta.env.VITE_DEEPSEEK_API_KEY;
if (!apiKey) {
console.error('❌ 错误:未找到 API Key,请检查 .env 文件及 VITE_ 前缀');
document.body.innerHTML = '<h1>配置错误:缺少 API Key</h1>';
throw new Error('Missing API Key');
}
// 🔴 3. 构建请求头
const headers = {
"Authorization": `Bearer ${apiKey}`, // 动态拼接 Token
'Content-Type': 'application/json' // 声明内容类型
};
// 🔴 4. 构建请求体 (Payload)
const payload = {
model: 'deepseek-chat', // 确认模型名称正确
messages: [
{
role: 'system', // System prompt 放前面,设定人设
content: '你是一个幽默风趣的技术专家,擅长用比喻解释复杂概念。'
},
{
role: 'user',
content: '你好,DeepSeek!请用一句话解释什么是闭包。'
}
],
temperature: 0.7 // 可选参数,控制创造性
};
// 🔴 5. 发起请求 (Async/Await 语法糖,比 .then() 链式调用更易读)
async function callLLM() {
const loadingEl = document.getElementById('loading');
const responseEl = document.getElementById('response');
if(loadingEl) loadingEl.style.display = 'block';
if(responseEl) responseEl.textContent = '';
try {
// 发起 fetch 请求,返回一个 Promise
const response = await fetch(endpoint, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload) // ⚠️ 必须 stringify!
});
// 🔴 6. 关键步骤:先检查 HTTP 状态码!
// fetch 只有在网络故障时才 reject,404/500 等错误不会抛出异常,需要手动判断
if (!response.ok) {
// 尝试读取错误信息,方便调试
const errorText = await response.text();
console.error('❌ API 响应错误:', response.status, errorText);
throw new Error(`请求失败:HTTP ${response.status}`);
}
// 解析 JSON 数据
const data = await response.json();
// 提取 AI 的回答
const aiContent = data.choices[0].message.content;
console.log('✅ 成功收到回复:', aiContent);
if(responseEl) {
responseEl.textContent = aiContent;
// 简单的打字机效果可以加在这里
}
} catch (error) {
console.error('❌ 请求过程出错:', error);
if(responseEl) {
responseEl.textContent = `出错了:${error.message}`;
responseEl.style.color = 'red';
}
} finally {
if(loadingEl) loadingEl.style.display = 'none';
}
}
// 绑定按钮事件(假设你有个 <button id="sendBtn">)
document.getElementById('sendBtn')?.addEventListener('click', callLLM);
// 页面加载自动调用一次测试
// callLLM();
🧐 代码亮点分析(面试/装逼专用)
import.meta.env: 这是 Vite 的魔法。它在构建时(Build Time)就把.env里的值替换进了代码里。用户在浏览器看到的源码里,Key 虽然还在,但至少你没把它明文写在 Git 仓库里。(注:严格来说,纯前端项目 Key 终究会暴露,生产环境建议加一层后端代理,后面会说)。async/await: 相比.then().catch()的回调地狱,这种写法像同步代码一样流畅,错误处理也用标准的try/catch,维护性满分。!response.ok检查: 很多新手直接await response.json(),如果接口报 401 (Key 错了) 或 500,response.json()可能会解析失败或者拿到错误对象导致后续代码崩溃。先判断状态码是成熟开发者的标志。JSON.stringify: 再次强调,HTTP 传输的是字符串流,JS 对象必须序列化。
🤔 灵魂拷问:这样 Key 就真的安全了吗?
真相时刻:😰
即使用了 .env,只要代码运行在用户的浏览器里,懂行的人打开 Network 面板或者查看打包后的 JS 文件,依然能看到那个 Key。
那 .env 有啥用?
- 防止误提交:避免你把 Key 硬编码在代码里推送到 GitHub,被扫描机器人秒删账号。
- 环境隔离:开发环境用测试 Key,生产环境用正式 Key,一套代码多处运行。
🚨 生产环境最佳实践(进阶):
如果你要上线商业项目,千万不要让前端直接调 LLM API!
正确架构:
前端 -> 你的后端(Node/Go/Python) -> LLM 厂商 API
- 前端只发请求给你的后端(不带 Key,或者带用户登录 Token)。
- 后端在服务器内部保存 Key,转发请求给 DeepSeek。
- 这样 Key 永远不出服务器,这才是真正的安全!
但在个人项目、原型验证、内网工具中,直接用 Vite + 前端调用是完全没问题的,效率最高!
📝 总结
- 工程化:用
npm create vite快速起步,别手写 HTML 了。 - 藏 Key:创建
.env文件,变量名必须加VITE_前缀,并加入.gitignore。 - 发请求:
- URL 用 HTTPS。
- Header 带上
Authorization: Bearer ...和Content-Type: application/json。 - Body 用
JSON.stringify()。
- 处理响应:
async/await真香,记得先判断response.ok。
💬 互动话题: 你在使用大模型 API 时,遇到过最奇葩的报错是什么?是“余额不足”还是“由于您太聪明,被系统判定为机器人”? 欢迎在评论区分享你的“踩坑”经历!点赞最高的送虚拟后端代理服务一份(我会用意念帮你转发请求)!🐶
💬 建议实操: 我们是否可以通过AI agent 直接solo把前端调用llm的项目直接转化为全栈项目使得 vite 实现呢?
作者:你的稀土掘金写作家 标签:#前端 #LLM #DeepSeek #Vite #JavaScript #API集成 #避坑指南