前端初探多模态 AI:用 Vite + 通义万象,几行代码实现"以图生图"
前言
大家好,今天想和你聊聊前端如何调用 AI 大模型这件事。
说到 AI,很多前端同学第一反应是"这是后端的事"、"我不懂 Python"、"又要学机器学习?"。其实不然——现在各大厂商都把 AI 能力封装成了标准的 HTTP API,前端只需要发请求、传参数、拿结果,和你平时调后端接口一模一样。
本文通过一个完整的 demo,带你从零搭建一个**"以图生图"**的前端项目:给 AI 三张图片 + 一句描述,AI 自动合成一张新图。核心技术栈:
- Vite:前端工程化脚手架
- 通义万象(Qwen Image):阿里云的多模态图像生成模型
- dotenv / .env:API Key 的安全管理
📌 适合人群:对 AI 感兴趣的前端同学,有 JS 基础即可,不需要任何机器学习背景。
一、先搞懂几个概念
1.1 什么是"多模态"(Multimodal)?
这是本文最重要的概念,先把它搞明白。
传统 AI 模型只能处理单一类型的数据:
- 纯文本模型(如 GPT)→ 只能理解文字
- 纯图像模型(如 ResNet)→ 只能理解图片
而多模态模型可以同时理解多种类型的数据:文字 + 图片 + 音频 + 视频...
传统模型:
文字 → [文本模型] → 文字输出
图片 → [图像模型] → 图片输出
多模态模型:
文字 + 图片 + 音频 → [一个多模态模型] → 文字/图片/音频
本文用到的**通义万象(Qwen Image)**就是一个多模态模型,它可以:
- 🖼️ 理解你上传的图片内容
- 📝 理解你的文字描述
- 🎨 将文字+图片信息融合,生成一张全新的图片
知识点:多模态模型的训练数据包含"图文对"(image-text pairs),模型学到了文字和图像之间的映射关系。所以你告诉它"把图1的人穿上图2的衣服",它真的能理解并执行。
1.2 前端调用 AI 的本质是什么?
说白了就三步:
前端 → HTTP 请求 → AI 服务商的 API → 返回结果 → 前端渲染
和你调任何后端接口完全一样。区别只在于:
- URL 变成了 AI 服务的地址
- 参数 里多了
model(选模型)、messages(对话/指令) - 认证 用
API Key而不是 cookie/session
// 你平时调后端接口
fetch('/api/user/info', {
headers: { 'Authorization': `Bearer ${token}` },
body: JSON.stringify({ userId: 123 })
})
// 调 AI 接口,换汤不换药!
fetch('https://dashscope.aliyuncs.com/api/v1/services/...', {
headers: { 'Authorization': `Bearer ${apiKey}` },
body: JSON.stringify({
model: 'qwen-image-2.0-pro',
input: { messages: [...] }
})
})
二、项目架构一览
先看整体结构,心里有个地图:
qwen-image-demo/
├── .env.local ← API Key 放在这里,不提交 Git
├── index.html ← 入口 HTML,挂载点 + 加载 main.js
├── package.json ← 项目配置,只依赖 vite
├── src/
│ ├── main.js ← 核心逻辑:调 AI 接口 + 渲染结果
│ ├── style.css ← Vite 模板自带的样式
│ └── counter.js ← 模板自带的计数器组件(本文不用)
项目极其轻量——只有一个核心文件 main.js,不到 60 行代码就完成了 AI 图像生成的全部逻辑。
三、第一步:Vite 搭建前端工程
3.1 Vite 是什么?
一句话:Vite 是新一代前端构建工具,由 Vue 的作者尤雨溪开发。它做了两件事:
- 开发时:用浏览器原生 ESM(ES Modules)加载代码,秒级启动,改了代码热更新快到飞起
- 打包时:用 Rollup 打包生产代码,又快又小
知识点:传统的 webpack 需要先把你写的所有代码"打包"成一个巨大的 bundle,才能在浏览器里跑。Vite 在开发模式下不打包——浏览器直接用
<script type="module">加载你的源文件,所以冷启动可以做到毫秒级。
对比一下启动速度:
webpack dev server: 🐢 启动 30s+,改一行代码等 2s
Vite dev server: 🚀 启动 < 1s,改一行代码 < 100ms
3.2 创建项目
npm create vite@latest qwen-image-demo
# 选 vanilla → JavaScript
cd qwen-image-demo
npm install
创建后的 package.json 极为简洁:
{
"name": "qwen-image-demo",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"vite": "^8.0.12"
}
}
注意两个关键配置:
"type": "module"→ 启用 ES Module,让你能用import.meta.envdevDependencies里只有vite→ 极致的轻量
3.3 入口 HTML 的秘密
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>qwen-image-demo</title>
</head>
<body>
<!-- 挂载点:JS 动态渲染的地方 -->
<div id="app"></div>
<!-- 关键!type="module" 告诉浏览器这是 ESM -->
<script type="module" src="/src/main.js"></script>
</body>
</html>
知识点:
<script type="module">是浏览器原生支持的 ES Module 加载方式。Vite 不需要你把所有 JS 打包成一个文件——浏览器会按 import 语句自己按需加载。这也是 Vite 开发模式"秒启动"的核心原因。
四、第二步:API Key 的安全管理
4.1 为什么不能写明文?
很多初学者会这样写:
// ❌ 危险!千万不要这样写!
const apiKey = 'sk-ws-H.REIHRXH.b4fF.MEUCIQC...'
这相当于把你的银行卡密码写在便利贴上贴在大街上。一旦提交到 GitHub:
- 别人可以直接用你的 Key 疯狂调用 API
- 阿里云会按调用量扣你账户的钱
- 已经有专门的爬虫机器人扫描 GitHub 上的 API Key
4.2 Vite 的环境变量机制
Vite 提供了一套优雅的解决方案——环境变量。
Step 1:创建 .env.local 文件,放入你的 API Key:
VITE_QWEN_API_KEY=sk-ws-H.你的真实Key
注意前缀必须是 VITE_,这是 Vite 的安全机制——只有 VITE_ 开头的变量才会暴露给前端代码。
知识点:为什么加
VITE_前缀?这是一种白名单机制。服务器可能有几十个环境变量(数据库密码、内部密钥等),如果全部暴露给前端就是灾难。Vite 只把VITE_前缀的变量注入到import.meta.env中,其余的环境变量前端根本看不到。
Step 2:确认 .gitignore 里排除了 .local 文件:
node_modules
dist
*.local ← 这一行确保 .env.local 不会被提交到 Git
Step 3:在代码中使用:
const apiKey = import.meta.env.VITE_QWEN_API_KEY;
// import.meta.env 是 Vite 在编译时注入的一个全局对象
// 类似 Webpack 的 process.env,但是基于 ESM 规范
4.3 整套流程回顾
你在 .env.local 里写 VITE_QWEN_API_KEY=xxx
↓
Vite 启动时读取 .env.local
↓
Vite 把 VITE_ 前缀的变量注入 import.meta.env
↓
你的代码 import.meta.env.VITE_QWEN_API_KEY 拿到值
↓
.gitignore 确保 .env.local 不进 Git 仓库
💡 一句话总结:VITE 就像前端项目的"大管家"——它管理着开发、构建、环境变量等一系列工程化事务,让你专心写业务代码。
五、第三步:核心代码逐行解析
下面是 main.js 的全部代码,我们逐段拆解:
5.1 获取 API Key
const apiKey = import.meta.env.VITE_QWEN_API_KEY;
const root = document.querySelector('#app');
import.meta.env.VITE_QWEN_API_KEY→ Vite 提供的编译时环境变量document.querySelector('#app')→ 拿到 HTML 里的挂载点<div id="app">
5.2 调用 AI 接口
const generateImage = async () => {
const res = await fetch(
'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
"model": "qwen-image-2.0-pro",
"input": {
"messages": [
{
"role": "user",
"content": [
{
"image": "https://help-static-aliyun-doc.aliyuncs.com/.../input1.png"
},
{
"image": "https://help-static-aliyun-doc.aliyuncs.com/.../input2.png"
},
{
"image": "https://help-static-aliyun-doc.aliyuncs.com/.../input3.png"
},
{
"text": "图1的女生穿着图2中的黑色裙子按图3的姿势坐下"
}
]
}
]
},
"parameters": {
"n": 1,
"size": "1024*1536"
}
})
}
)
const data = await res.json();
console.log(data);
return data.output.choices[0].message.content[0].image;
}
这段是本文的灵魂代码,我们拆开细说:
📌 接口地址
https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation
这是阿里云 DashScope(百炼平台)的多模态生成接口。"dashscope" 是阿里的 AI 模型服务平台,类似于 OpenAI 的 platform.openai.com。
📌 认证方式
'Authorization': `Bearer ${apiKey}`
Bearer Token 是 AI API 最常用的认证方式。你的 API Key 就是"通行证",放到请求头的 Authorization 字段里即可。
知识点:Bearer Token vs API Key
- API Key 通常是一个固定字符串,直接用来标识"谁在调用"
- Bearer Token 是 OAuth 2.0 标准中的一种令牌类型,放在
Authorization: Bearer xxx头里- 实际使用中,多数 AI 服务商把 API Key 直接当 Bearer Token 用
📌 请求体结构
body: JSON.stringify({...})
知识点:HTTP 请求体在传输过程中是二进制字节流。
JSON.stringify()的作用是把 JS 对象序列化成 JSON 字符串,然后浏览器会把这个字符串编码为二进制发送。服务端收到后做反向操作——反序列化,还原成对象。
整个请求体分为三部分:
| 部分 | 作用 |
|---|---|
model | 指定用哪个模型。这里用的是 qwen-image-2.0-pro(通义万象 2.0 Pro 版) |
input.messages | 传给 AI 的"提示词",格式类似 ChatGPT 的对话结构 |
parameters | 生成参数。n: 1 生成一张,size 指定图片尺寸 |
📌 messages 的格式——多模态的关键
"content": [
{ "image": "https://...图片1的URL" }, // 图1:一个女生
{ "image": "https://...图片2的URL" }, // 图2:一条黑色裙子
{ "image": "https://...图片3的URL" }, // 图3:一个坐姿
{ "text": "图1的女生穿着图2中的黑色裙子按图3的姿势坐下" } // 文字指令
]
注意 content 是一个数组而不是字符串!这是多模态模型的标志性特征:
- 纯文本模型:
content: "你好"(字符串) - 多模态模型:
content: [{image: "..."}, {text: "..."}](数组,可以混合多种类型)
AI 会理解:
- 图1 里有谁 → 一个女生
- 图2 里有什么 → 一条黑色裙子
- 图3 是什么姿势 → 一个坐着的姿势
- 文字说什么 → 把这三者组合起来
然后生成一张融合了三张参考图 + 文字指令的全新图片。这就是多模态的魅力。
📌 解析返回结果
const data = await res.json();
return data.output.choices[0].message.content[0].image;
返回的 JSON 结构大概是这样:
{
"output": {
"choices": [
{
"message": {
"content": [
{ "image": "https://...生成的图片URL" }
]
}
}
]
}
}
路径拆解:
output.choices→ AI 生成的候选结果列表[0]→ 取第一个(我们n:1所以只有一个)message.content→ 多模态返回的内容数组[0].image→ 第一项内容的图片 URL
5.3 渲染结果
const renderImage = (imageUrl) => {
root.innerHTML = `<img src="${imageUrl}" />`
}
拿到 AI 返回的图片 URL 后,动态插入到页面中。简单直接。
5.4 入口
const main = async () => {
const imageUrl = await generateImage(); // 调 AI 生成图片
renderImage(imageUrl); // 把图片显示到页面上
}
main();
六、完整数据流
把整个流程串起来看:
┌──────────────────────────────────────────────┐
│ 浏览器 │
│ │
│ 1. 加载 index.html │
│ 2. <script type="module"> 加载 main.js │
│ 3. import.meta.env 读取 API Key │
│ 4. fetch() 发 POST 请求到阿里云 │
│ ↓ │
│ 5. 阿里云收到请求,通义万象模型处理 │
│ - 分析图1、图2、图3的内容 │
│ - 理解文字指令 │
│ - 融合生成新图片 │
│ ↓ │
│ 6. 返回生成的图片 URL 给浏览器 │
│ 7. renderImage() 把图片渲染到 <div id="app"> │
└──────────────────────────────────────────────┘
七、关键知识点总结
7.1 前端工程化
| 概念 | 一句话解释 |
|---|---|
| Vite | 新一代前端构建工具,开发秒启动,打包用 Rollup |
| ESM(ES Module) | 浏览器原生模块化方案,import/export 语法 |
<script type="module"> | 告诉浏览器这个脚本是 ESM 模块 |
import.meta.env | Vite 注入的全局环境变量对象,只有 VITE_ 前缀的变量能用 |
.env.local | 本地环境变量文件,不提交 Git |
7.2 AI / 多模态
| 概念 | 一句话解释 |
|---|---|
| 多模态(Multimodal) | 一个模型同时处理文字、图片、音频等多种类型的数据 |
| 通义万象(Qwen Image) | 阿里云的多模态图像生成模型 |
| API Key | 调用 AI 服务的"通行证",需要保密 |
| messages | 传给 AI 的对话/指令,多模态模型的 content 是数组格式 |
| Bearer Token | HTTP 认证方式,把 Key 放在 Authorization: Bearer xxx 头里 |
7.3 安全
| 概念 | 一句话解释 |
|---|---|
.gitignore | 告诉 Git 哪些文件不提交,.env.local 必须在里面 |
VITE_ 前缀 | Vite 的安全机制,只有这个前缀的变量才暴露给前端 |
| 不要在代码里写明文 Key | 写在 .env 文件里,用环境变量读取 |
八、动手实践建议
读到这里,建议你现在就动手试试——看懂和能写是两回事:
- 跑通这个 demo:去阿里云百炼平台申请 API Key → 创建 Vite 项目 → 换成你自己的 Key →
npm run dev跑起来 - 改一改:换几张参考图片,换一段文字描述,看 AI 生成的结果有什么变化
- 拓展思考:
- 如果想把生成的图片展示得更美观,CSS 怎么写?
- 如果想加上 loading 状态和错误处理,代码怎么改?
- 如果用户能自己上传图片,你打算怎么做?
🌟 进阶提示:AI 接口的返回值通常需要 5-30 秒(图片生成特别慢),实际项目中记得加 loading 状态和超时处理。另外,真正上线的项目应该把 API 调用放在后端做代理——这样 API Key 完全不会暴露到浏览器里。
写在最后
前端做 AI 开发没有想象中那么遥不可及。现在的 AI 服务商(阿里、OpenAI、Claude 等)都把能力封装成了标准的 HTTP API——会用 fetch 就能写 AI 应用。
本文只是一个起点。从这个 demo 出发,你可以:
- 接入文本生成(GPT/文心一言/通义千问)→ 做智能客服、文章摘要
- 接入语音识别 → 做语音助手
- 接入视频理解 → 做视频内容分析
AI 时代的门槛是 API 调用,而不是微调模型。 前端大有可为,一起加油 💪
如果你觉得这篇文章有帮助,欢迎点赞、收藏、关注~ 有疑问欢迎在评论区交流!