从零到一:用 Vite 调用阿里百炼 Qwen Image 多模态生图,前端工程化 AI 全流程实战前言:AI 多模态时
前言:AI 多模态时代,前端能做的不只是"调接口"
2026 年了,“前端调 AI API"这件事已经不是什么新鲜事。但如果你的认知还停留在"前端发个fetch、拿个文本回显到页面上”——那你可能错过了多模态这个更有想象力的方向。

多模态(Multimodal) 是 2025-2026 年 AI 领域最大的突破之一。模型不再只是"输入文字、输出文字",而是能理解图片、生成图片、甚至图文混合输入。这意味着:
- 你给模型两张穿搭图 + 一句文字描述 → 它生成一张融合后的效果图
- 你给模型一张产品图 + 一段风格描述 → 它生成电商场景图
- 你给模型一张草图 + 一段文字 → 它生成渲染后的成品
这张图片正是我调用多模态生成的结果!!!
本文将从零开始,带你完成一个完整的多模态生图前端项目:
- 在阿里百炼平台(Bailian)获取 API Key
- 使用 Vite 搭建前端项目脚手架
- 用
.env.local安全管理 API Key(前端环境密钥保护的核心方案) - 配置 Vite 代理转发解决跨域(CORS)问题
- 调用 Qwen Image 2.0 Pro(通义万象)多模态生图 API
- 实现完整的 错误处理 和图片渲染逻辑
读完本文,你将掌握前端工程化 + AI 多模态的完整开发链路。
本文是「AI 全栈路线」系列第三篇——前两篇分别覆盖了 [Node.js + DeepSeek 后端调用] 和 [Python + AI 实战],本篇聚焦前端工程化视角下的多模态 AI 调用。
一、先拿到钥匙:阿里百炼平台获取 API Key
1.1 百炼是什么?
阿里百炼(Bailian) 是阿里云推出的大模型服务平台,上面汇集了阿里系的全线 AI 模型能力:
| 能力类型 | 代表性模型 | 说明 |
|---|---|---|
| 文本对话 | Qwen3(通义千问) | 对标 GPT-4 的国产大模型 |
| 多模态生图 | Qwen Image 2.0 Pro | 本文主角——图文混合输入、可控生成 |
| 语音识别 | Paraformer | ASR 语音转文字 |
| 向量化 | Text Embedding | RAG 场景必备 |
百炼的定位:一个平台管理所有模型,一套 API Key 调用所有能力。
1.2 获取 API Key
进入 百炼控制台 API Key 页面:
- 登录阿里云账号(支付宝扫码即可)
- 点击「创建 API Key」
- 复制生成的 Key(形如
sk-ws-H.xxxxxxxx)
⚠️ 重要:API Key 只会显示一次,请立即保存到安全位置。这个 Key 代表你的账户身份,泄露意味着别人可以盗刷你的额度。
百炼的控制台还提供了模型广场(按分类浏览所有可用模型)、在线体验(无需写代码直接测试模型效果)和用量监控——这些功能在你选型和调试模型时会经常用到。
二、项目初始化:Vite 脚手架 + 依赖
2.1 为什么用 Vite?
前端发展到 2026 年,Vite 已经成为事实标准的项目构建工具。它的核心优势:
- 原生 ESM 开发服务器——冷启动毫秒级
- HMR(热模块替换)——改代码浏览器秒级更新
- 内置代理配置——一行配置解决开发环境跨域
.env开箱即用——环境变量管理零配置
对于 AI 前端项目,Vite 还有一个关键价值:它通过 import.meta.env 机制,保证 API Key 在构建时注入但不会暴露到生产代码中。 这一点后面会详细展开。
2.2 创建项目
npm init vite@latest qwen-image-demo
# 选择 Vanilla + JavaScript
cd qwen-image-demo
npm install
生成的项目结构:
qwen-image-demo/
├── .env.local # API Key 存放处(不提交 Git)
├── .gitignore # 声明忽略文件
├── index.html # HTML 入口
├── package.json # 项目配置
├── vite.config.js # Vite 配置文件
├── public/ # 静态资源(不参与构建处理)
└── src/
└── main.js # 业务入口
依赖只有一个:
{
"devDependencies": {
"vite": "^8.0.12"
}
}
零运行时依赖。 你会发现整个项目没有装 openai、没有装 axios、也没有装任何 AI SDK——我们用原生 fetch + JSON 直接调用 DashScope API。如果你刻意要学,永远从最原始的方式开始,不要一上来就被框架包一层。
三、API Key 安全管理:前端环境的关键防护方案
3.1 前端 Key 的困境
后端项目可以通过 dotenv → process.env 管理密钥,Key 存储在服务器上,用户看不见。
前端不一样——所有代码最终都会跑到用户浏览器里。 如果你写了:
// ❌ 绝对不要这样做!你的 Key 等于公开发布
const apiKey = 'sk-ws-H.xxxxxxxxxxxxxx';
那你的 API Key 就相当于公开了——任何人打开浏览器 DevTools → Sources 面板就能看到。额度被盗刷只是时间问题。
3.2 .env.local + import.meta.env:Vite 的安全方案
Vite 提供了环境变量机制,格式要求:变量名必须以 VITE_ 开头。
创建 .env.local:
# .env.local
VITE_QWEN_API_KEY=sk-ws-H.RPRRIMR.nBdD...你的真实Key
代码中访问:
// src/main.js
const apiKey = import.meta.env.VITE_QWEN_API_KEY;
// 构建时 Vite 会把它替换成真实值
核心机制:
.env.local 文件
↓ (Vite 构建时读取)
被内联替换到 import.meta.env.VITE_QWEN_API_KEY
↓
业务代码中拿到的是字符串值
🔑 关键理解:
import.meta.env不是运行时读取文件——它是构建时静态替换。Vite 在打包时把你的.env.local里的值直接内联到代码中。这意味着:
- 开发时你能用这个 Key 调试
- 生产构建时,如果你没在部署环境配置这个变量,构建出来的代码里就没有这个 Key
.env.local已经在.gitignore中被忽略(Vite 脚手架默认配置),不会提交到 Git
3.3 安全边界:代理才是最终的防线
不过要诚实地说:即使是 import.meta.env,在开发模式下 Key 依然会出现在浏览器内存中。 更安全的架构是:
浏览器 → Vite Dev Server(代理)→ DashScope API
↑ 持有 API Key
浏览器不直接接触 Key
这个方案的核心是——API Key 只存在 Vite Dev Server 这一层,浏览器发出的请求本身不带 Key,由代理层注入。 本文会同时展示两种方案,帮助你理解各自适用场景。
3.4 .gitignore 保护
Vite 脚手架生成的 .gitignore 已经包含了关键规则:
node_modules
dist
*.local # ← 这条规则保证了 .env.local 不会被提交
*.local 这个通配符匹配任何以 .local 结尾的文件。 所以 .env.local、.env.development.local 等都会被 Git 忽略。本地和远程的边界清晰分离——这是工程化的基本素养。
四、Vite 代理配置:解决跨域问题
4.1 问题:浏览器直接请求会被 CORS 拦截
DashScope API 的地址是 https://dashscope.aliyuncs.com。如果你从前端直接 fetch 这个地址:
// ❌ 浏览器会报 CORS 错误
fetch('https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation')
控制台会提示:
Access to fetch at 'https://dashscope.aliyuncs.com/...' from origin 'http://localhost:5173'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header...
原因:浏览器的同源策略(Same-Origin Policy)禁止一个 origin(localhost:5173)向另一个 origin(dashscope.aliyuncs.com)发请求,除非目标服务器明确返回了允许跨域的响应头。阿里云 API 服务器不会给 localhost 发这个头——否则任何人都能在浏览器里直接调它了。
4.2 解决方案:Vite Server Proxy
Vite 的 server.proxy 配置可以在开发服务器上架一座桥:
// vite.config.js
import { defineConfig } from 'vite'
export default defineConfig({
server: {
proxy: {
'/api/dashscope': {
target: 'https://dashscope.aliyuncs.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/dashscope/, ''),
},
},
},
})
这段配置做了什么? 逐行解释:
| 配置项 | 说明 |
|---|---|
'/api/dashscope' | 匹配所有以 /api/dashscope 开头的请求路径 |
target | 将匹配到的请求转发到这个地址 |
changeOrigin: true | 把请求头中的 Origin 改成 target 的域名(避免目标服务器拒绝) |
rewrite | 路径重写——把 /api/dashscope 前缀去掉后再发给目标 |
请求流转过程:
浏览器发起:
fetch('/api/dashscope/api/v1/services/aigc/multimodal-generation/generation')
↓
Vite Dev Server 拦截(匹配到 /api/dashscope 前缀)
↓
去掉前缀 /api/dashscope,变成:
/api/v1/services/aigc/multimodal-generation/generation
↓
转发到:https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation
↓
拿到响应 → 返回给浏览器
结果:从浏览器的视角看,请求是发给 localhost:5173(同源),不存在跨域问题。代理层在服务端帮你走了跨域请求——服务端不受浏览器同源策略限制。
💡 这是前端工程化的经典模式:Vite 就是前端项目在工程化这块的"大管家"——Dev Server、HMR、构建打包、代理转发、环境变量,全部由它接管。
npm run dev一启动,Vite 就接管了整个项目的开发环境。
五、核心实战:调用 Qwen Image 多模态生图 API
5.1 理解多模态生图
传统的文生图(Text-to-Image)只支持一段文字 → 一张图。多模态生图支持多张参考图 + 文字描述 → 一张融合图。
用本文示例来理解:
- 输入:图1(一个女生)+ 图2(一条黑裙子)+ 图3(一个坐姿)+ 文字描述"图1的女生穿着图2中的黑色裙子按图3的姿势坐下"
- 输出:一张融合了三张参考图 + 文字指令的生成图
这个能力背后是 Qwen Image 2.0 Pro 模型——阿里通义万象系列的最新版本,专门做可控的多模态图像生成。
5.2 完整代码
// src/main.js
// 从 Vite 环境变量中获取 API Key(构建时内联替换)
const apiKey = import.meta.env.VITE_QWEN_API_KEY;
// 获取页面挂载点
const root = document.querySelector('#app');
// 核心函数:调用多模态生图 API
const generateImage = async () => {
const res = await fetch(
// 注意:这里请求的是 Vite 代理路径,不是直接请求 dashscope
'/api/dashscope/api/v1/services/aigc/multimodal-generation/generation',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
// API Key 通过 Authorization 头携带
'Authorization': `Bearer ${apiKey}`,
},
// 请求体:序列化为 JSON 字符串后以二进制传输
body: JSON.stringify({
"model": "qwen-image-2.0-pro",
"input": {
"messages": [
{
"role": "user",
"content": [
{
// 图1:参考人物
"image": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250925/thtclx/input1.png"
},
{
// 图2:参考服饰
"image": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250925/iclsnx/input2.png"
},
{
// 图3:参考姿势
"image": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250925/gborgw/input3.png"
},
{
// 文字指令
"text": "图1的女生穿着图2中的黑色裙子按图3的姿势坐下"
}
]
}
]
},
"parameters": {
"n": 1, // 生成几张图
"size": "1024*1536" // 输出尺寸(宽*高)
}
})
}
);
// 解析 JSON 响应
const data = await res.json();
// 错误处理:HTTP 状态码非 2xx 时给用户反馈
if (!res.ok) {
root.innerHTML = `<p style="color:red">请求失败(${res.status}): ${data.message || '请稍后再试'}</p>`;
return '';
}
// 从深层嵌套结构中取出生成的图片 URL
return data.output.choices[0].message.content[0].image;
}
// 渲染图片到页面
const renderImage = (imageUrl) => {
root.innerHTML = `<img src="${imageUrl}" alt="AI生成的图片" />`;
}
// 单点入口
const main = async () => {
const imageUrl = await generateImage();
if (imageUrl) {
renderImage(imageUrl);
}
};
main();
5.3 逐层解析请求结构
请求路径
/api/dashscope/api/v1/services/aigc/multimodal-generation/generation
这不是 DashScope 的真实地址——它会被 Vite 代理拦截,去掉 /api/dashscope 前缀后转发到:
https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation
请求体结构
多模态生图 API 的 messages 结构比文本对话更丰富——content 是一个数组,元素可以是 image(图片 URL)或 text(文字指令):
"content": [
{ "image": "https://..." }, // 多张参考图片
{ "image": "https://..." },
{ "image": "https://..." },
{ "text": "图1的女生穿着图2中的黑色裙子按图3的姿势坐下" } // 文字指令
]
模型会根据所有 content 元素的内容综合理解你的意图。 图片 + 文字的混合输入,是多模态和传统文生图最本质的区别。
参数说明
| 参数 | 说明 | 本文取值 |
|---|---|---|
model | 模型名称 | qwen-image-2.0-pro |
input.messages[].content[].image | 参考图片 URL | 阿里云文档的示例图 |
input.messages[].content[].text | 文字指令 | 描述如何使用参考图 |
parameters.n | 生成图片数量 | 1 |
parameters.size | 输出尺寸 | 1024*1536(竖版) |
六、错误处理:从 404 到 CORS 的踩坑实录
6.1 问题:API 返回非成功状态时前端直接崩溃
在最初版本中,代码拿到 res.json() 后就直接取 data.output.choices[0]...。但如果 API 返回了错误(如 401 认证失败、400 参数错误、404 地址写错),data.output 是 undefined——代码直接抛 TypeError 白屏。
6.2 修复:if (!res.ok) 守卫
const data = await res.json();
// ✅ 在访问深层属性之前,先检查 HTTP 状态
if (!res.ok) {
root.innerHTML = `<p style="color:red">请求失败(${res.status}): ${data.message || '请稍后再试'}</p>`;
return '';
}
// 只有请求成功时才访问 output 下的数据结构
return data.output.choices[0].message.content[0].image;
res.ok 是什么? fetch 返回的 Response 对象上有一个 ok 属性——当 HTTP 状态码在 200-299 范围内时为 true,否则为 false。这是判断请求是否成功的标准方式。
这条守卫语句覆盖了所有这些常见错误场景:
| 场景 | HTTP 状态码 | res.ok | 用户看到 |
|---|---|---|---|
| API Key 错误 | 401 | false | “请求失败(401): Invalid API Key” |
| 参数格式错误 | 400 | false | “请求失败(400): …” |
| 路径写错 | 404 | false | “请求失败(404): …” |
| 请求成功 | 200 | true | 正常渲染图片 |
6.3 开发过程中的常见坑
-
CORS 错误(
No 'Access-Control-Allow-Origin' header)- 原因:浏览器直接请求跨域地址
- 解决:Vite proxy 配置(见第四章)
- 注意:Vite proxy 仅在
npm run dev开发模式生效,生产环境需要在部署层配置反向代理
-
404 Not Found
- 原因:请求路径写错,常见的是代理前缀和目标路径拼接错误
- 排查:在 Vite 配置中加
configure打印日志确认实际请求路径 - 解决:确保
rewrite规则正确去掉前缀
-
401 Unauthorized
- 原因:API Key 错误或过期
- 排查:确认
.env.local中的 Key 是最新的、格式正确(sk-开头) - 注意:百炼的 Key 和灵积(DashScope)旧版 Key 格式不同
七、异步流程控制:async/await 让代码像说话一样
7.1 为什么用 async/await
看三种写法的对比:
// ❌ 回调写法——嵌套地狱
fetch(url, options, (err, res) => {
if (err) return;
res.json((err, data) => {
if (err) return;
renderImage(data); // 回调套回调
});
});
// ⚠️ Promise 链——可读性中等
fetch(url, options)
.then(res => res.json())
.then(data => renderImage(data))
.catch(err => console.error(err));
// ✅ async/await——像读小说一样自上而下
const res = await fetch(url, options);
const data = await res.json();
renderImage(data);
await 不会阻塞浏览器 UI 线程——它只暂停当前 async 函数的执行,事件循环照常运行。所以 await 等待 API 返回期间,页面不会卡住。
7.2 本文中的完整异步流程
const main = async () => {
const imageUrl = await generateImage(); // ← 卡在这里等生图结果
// generateImage 内部:
// await fetch(...) ← 等 HTTP 响应
// 然后 await res.json() ← 等 JSON 解析
if (imageUrl) {
renderImage(imageUrl); // ← 拿到结果后才渲染
}
};
main();
执行时间线:
0ms → main() 执行
1ms → generateImage() 内部发起 fetch 请求
... → 等待网络往返 + 模型推理(可能 3-15 秒)
N ms → 拿到响应 → 解析 JSON → 取出图片 URL → 返回
N+1ms → renderImage(imageUrl) → 图片显示在页面上
整个过程 UI 线程不阻塞,用户操作正常响应
八、完整的数据流转:从百炼 Key 到页面图片
回顾整个项目的全链路:
┌─────────────────────────────────────────────────────┐
│ 1. 阿里百炼控制台 │
│ https://bailian.console.aliyun.com │
│ → 创建 API Key → 复制 sk-ws-... │
└────────────────────────┬────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 2. .env.local (本地文件,不提交 Git) │
│ VITE_QWEN_API_KEY=sk-ws-... │
└────────────────────────┬────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 3. Vite 构建层 │
│ import.meta.env.VITE_QWEN_API_KEY │
│ → 构建时静态替换为真实值 │
└────────────────────────┬────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 4. 浏览器 → Vite Dev Server (localhost:5173) │
│ fetch('/api/dashscope/...') │
│ 携带 Header: Authorization: Bearer sk-ws-... │
└────────────────────────┬────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 5. Vite Proxy 拦截 │
│ 匹配 /api/dashscope 前缀 │
│ → 去掉前缀 → 转发到 dashscope.aliyuncs.com │
└────────────────────────┬────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 6. DashScope API(阿里云端) │
│ Qwen Image 2.0 Pro 模型推理 │
│ 输入:3张参考图 + 文字指令 │
│ 输出:融合后的图片 URL │
└────────────────────────┬────────────────────────────┘
↓
┌─────────────────────────────────────────────────────┐
│ 7. 响应返回 → 错误检查 (!res.ok) │
│ → 解析 JSON → 取出 images[0].url │
│ → innerHTML 渲染到 #app │
│ → 用户看到生成的图片 ✅ │
└─────────────────────────────────────────────────────┘
七个环节,每一步都有它的工程意义。 不是"能跑就行"——而是要理解每一步为什么这样设计。
九、延伸的一些知识:前端 AI 开发的更多可能方向
掌握了这个基础骨架后,可以沿着这些方向深入:
| 方向 | 说明 | 关联技术 |
|---|---|---|
| 流式生成 | 图片生成过程中的进度展示 | SSE / WebSocket |
| 历史记录 | 保存生成历史,支持回看 | localStorage / IndexedDB |
| 参数调优面板 | 让用户在页面上调整 size、n、negative prompt 等参数 | UI 控件 + 动态构造 body |
| 图片上传替代 URL | 支持用户本地上传图片作为参考图 | File API + OSS 上传后传 URL |
| 生产部署 | Nginx 反向代理替代 Vite proxy | Nginx + 环境变量管理 |
| 多模型切换 | 同时调用多个生图模型做对比 | 抽象 API 层 + 策略模式 |
十、总结:前端工程化 + AI 的核心套路
本文用一整个实战项目串起了这些知识点:
技术要点清单
- 阿里百炼平台(bailian.console.aliyun.com)——获取 API Key 的入口,阿里全系 AI 模型的管理中心
- Vite 项目脚手架 ——
npm init vite,零配置启动前端工程 .env.local+import.meta.env—— 前端环境变量的安全管理方案,VITE_前缀是关键- Vite Server Proxy ——
server.proxy配置解决 CORS 跨域问题,rewrite做路径重写 - Qwen Image 2.0 Pro 多模态 API ——
messages的content数组中混合image+text,实现图文融合生图 fetch+ JSON —— 不需要任何 AI SDK,原生 API 直接调用!res.ok错误守卫 —— 在访问深层嵌套数据前先检查 HTTP 状态,防止白屏async/await异步控制 —— 让异步代码像同步代码一样自上而下可读
核心心智模型
百炼获取Key
.env.local 安全存储
Vite import.meta.env 注入
fetch 调用 API
Vite Proxy 转发跨域
DashScope 模型推理
错误检查 res.ok
渲染结果到页面
🔥 前端工程化 + AI = 这个时代前端开发者的核心竞争力。
Vite 是你的"大管家"——它接管 Dev Server、环境变量、构建打包、代理转发。AI API 是你的"能力放大器"——以前一个设计师团队花一周做的图,现在一个前端工程师 10 秒调一次 API 就搞定了。
不是 AI 取代前端,是会用 AI 的前端取代不会用的。
如果这篇文章对你有帮助,欢迎点赞、收藏、评论。有问题也可以在评论区交流!
更多推荐



所有评论(0)