从零搭建 AI 生图前端|Vite 工程化 + 通义千问 API 实战,附 API Key 安全方案

从零搭建 AI 生图前端:Vite 工程化 + 通义千问 Image API 实战

本文带你从零开始,用 Vite 脚手架搭建一个轻量前端项目,通过 HTTP 调用阿里云 DashScope 的通义千问(Qwen)图像生成 API,并深入探讨前端环境变量管理的最佳实践。


项目概览

这是一个基于 Vite 的前端 Demo,核心功能是:

  1. 用户打开页面
  2. 前端直接通过 fetch 调用阿里云 DashScope 的多模态生成接口
  3. 传入参考图 + 文字描述,让 Qwen 模型生成一张新图片
  4. 将生成的图片渲染到页面上

听起来很简单?但这里涉及一个关键问题:API Key 写在前端代码里会直接暴露。那在前端工程化体系下,怎么安全地管理 API Key 呢?这正是本文的重点。


项目结构

csharp 复制代码
qwen-image-demo/
├── index.html              # 入口 HTML
├── package.json            # 项目配置
├── .env.local              # 环境变量(API Key 存放处)
├── .gitignore              # Git 忽略规则
├── public/
│   ├── favicon.svg         # 网站图标
│   └── icons.svg           # SVG 图标精灵
└── src/
    ├── main.js             # 主逻辑:调用 API + 渲染
    ├── style.css           # 全局样式
    └── assets/             # 静态资源
        ├── hero.png
        ├── javascript.svg
        └── vite.svg

第一步:Vite 初始化

bash 复制代码
npm create vite@latest qwen-image-demo
cd qwen-image-demo
npm install

package.json 核心配置:

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.env


第二步:环境变量管理 ------ 本文核心

错误做法

很多人刚开始会这样写:

js 复制代码
// 危险!绝对不要这样做!
const apiKey = 'sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

API Key 明文写在前端代码里,任何人打开浏览器 DevTools 就能看到。即使你用了 Webpack/Vite 打包,字符串依然是明文的。

✅ 正确做法:Vite 环境变量

Vite 提供了内置的环境变量机制。在项目根目录 创建 .env.local 文件:

bash 复制代码
# .env.local
VITE_QWEN_API_KEY=sk-your-api-key-here

两条铁律

  1. 变量名必须以 VITE_ 开头,否则 Vite 不会暴露给客户端代码
  2. .env.local 必须加入 .gitignore绝对不能提交到 Git 仓库

在代码中使用:

js 复制代码
const apiKey = import.meta.env.VITE_QWEN_API_KEY;

Vite 在构建时会将 import.meta.env.VITE_* 静态替换为实际值。开发时通过 Vite Dev Server 注入,生产构建时内联到 bundle 中。

Vite 就是前端项目在工程化这块的"大管家" 。当你执行 npm run dev 的那一刻,Vite 接管了整个项目:模块热更新、环境变量注入、ESM 原生支持、CSS 处理......全部由它统一调度。

安全边界

这里有一个重要的认知:环境变量最终还是会出现在前端 bundle 中VITE_* 变量的设计目的是「避免将密钥硬编码在源码中并提交到 Git」,而不是「让前端密钥对用户不可见」。

真正安全的做法是:

  • 生产环境:API Key 只存放在后端,前端通过自己的后端中转请求
  • 开发/Demo 阶段 :使用 Vite 环境变量 + .gitignore 防止密钥泄露到代码仓库

第三步:入口 HTML

html 复制代码
<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Qwen 图像生成 Demo</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

关键点:

  • <script type="module"> 启用浏览器原生 ESM,Vite 在开发阶段直接利用这个能力实现极速 HMR
  • #app 是挂载点,所有 DOM 操作都围绕它展开

第四步:核心逻辑 main.js

原版代码存在的问题

项目原始代码存在以下不足:

  1. 缺少 CSS 导入 ------ 写了 style.css 但没有在 JS 中 import,样式不会生效
  2. 无错误处理 ------ API 调用失败时没有任何提示,直接白屏
  3. 无响应校验 ------ 直接链式访问深层属性,任一环节为空都会导致运行时崩溃
  4. 加载状态缺失 ------ 图片生成需要数秒,用户看不到任何反馈

修正后的完整代码

js 复制代码
// 导入样式(原版缺失,导致 CSS 不生效)
import './style.css';

const apiKey = import.meta.env.VITE_QWEN_API_KEY;
const root = document.querySelector('#app');

/**
 * 调用 DashScope 多模态生成接口生成图片
 * @returns {Promise<string>} 生成的图片 URL 或 base64
 */
const generateImage = async () => {
  const res = await fetch(
    // 替换为你的 DashScope 多模态生成端点,格式见官方文档
    'https://<your-dashscope-endpoint>/api/v1/services/aigc/multimodal-generation/generation',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${apiKey}`,
      },
      body: JSON.stringify({
        model: 'qwen-image-plus',   // 模型名称,按实际选用
        input: {
          messages: [
            {
              role: 'user',
              content: [
                {
                  image: 'https://example.com/ref-1.png'   // 参考图 1
                },
                {
                  image: 'https://example.com/ref-2.png'   // 参考图 2
                },
                {
                  image: 'https://example.com/ref-3.png'   // 参考图 3
                },
                {
                  text: '参考图1的人物穿着图2的衣服,摆出图3的姿势'
                }
              ]
            }
          ]
        },
        parameters: {
          n: 1,
          size: '1024*1024'
        }
      })
    }
  );

  // 新增:HTTP 状态码校验
  if (!res.ok) {
    const errBody = await res.text();
    throw new Error(`API 请求失败 (${res.status}): ${errBody}`);
  }

  const data = await res.json();
  console.log('📦 API 响应:', data);

  // 新增:安全访问深层属性
  const imageUrl = data?.output?.choices?.[0]?.message?.content?.[0]?.image;
  if (!imageUrl) {
    throw new Error('响应中未找到生成的图片,请检查 API 返回结构');
  }

  return imageUrl;
};

/**
 * 将图片渲染到页面
 */
const renderImage = (imageUrl) => {
  root.innerHTML = `
    <div style="padding: 40px 20px;">
      <h2>🎨 生成结果</h2>
      <img
        src="${imageUrl}"
        alt="AI 生成的图片"
        style="max-width: 400px; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.15);"
      />
    </div>
  `;
};

/**
 * 渲染错误信息
 */
const renderError = (message) => {
  root.innerHTML = `
    <div style="padding: 40px 20px; color: #e53e3e;">
      <h2>❌ 生成失败</h2>
      <p>${message}</p>
    </div>
  `;
};

/**
 * 渲染加载状态
 */
const renderLoading = () => {
  root.innerHTML = `
    <div style="padding: 40px 20px;">
      <h2>⏳ 正在生成图片...</h2>
      <p>请稍候,这可能需要几秒钟</p>
    </div>
  `;
};

// ✅ 主流程:完整的加载 → 结果 / 错误 状态管理
const main = async () => {
  try {
    renderLoading();
    const imageUrl = await generateImage();
    renderImage(imageUrl);
  } catch (err) {
    console.error('生成图片出错:', err);
    renderError(err.message);
  }
};

main();

第五步:样式

样式文件 src/style.css 定义了 CSS 变量、响应式布局、暗色模式等。有几个值得注意的设计细节:

CSS 变量体系

css 复制代码
:root {
  --text: #6b6375;
  --text-h: #08060d;
  --bg: #fff;
  --border: #e5e4e7;
  --accent: #aa3bff;
  --accent-bg: rgba(170, 59, 255, 0.1);
  --shadow: rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;

  font: 18px/145% var(--sans);
  color-scheme: light dark;  /* 声明支持明暗双模式 */
}

暗色模式适配

css 复制代码
@media (prefers-color-scheme: dark) {
  :root {
    --text: #9ca3af;
    --bg: #16171d;
    --accent: #c084fc;
    /* ... */
  }
}

通过 prefers-color-scheme 媒体查询,自动跟随系统主题切换,无需 JS 干预。

注意 :CSS 必须通过 import './style.css' 在 JS 中导入才能被 Vite 打包和处理。这是原版代码遗漏的关键一步。


完整数据流

scss 复制代码
用户打开页面
    │
    ▼
main.js 执行
    │
    ├── import './style.css'          ← Vite 处理 CSS
    ├── import.meta.env.VITE_*        ← Vite 注入环境变量
    │
    ▼
renderLoading()  →  显示"正在生成..."
    │
    ▼
generateImage()
    │
    ├── fetch() POST → DashScope API
    │   ├── Authorization: Bearer ${apiKey}
    │   └── Body: { model, input: { messages }, parameters }
    │
    ├── res.ok?  ──No──→ throw Error → renderError()
    │
    ▼
data.output.choices[0].message.content[0].image
    │
    ▼
renderImage()  →  页面展示生成的图片

API 请求/响应详解

请求结构

json 复制代码
{
  "model": "qwen-image-plus",
  "input": {
    "messages": [{
      "role": "user",
      "content": [
        { "image": "https://example.com/ref-1.png" },
        { "image": "https://example.com/ref-2.png" },
        { "image": "https://example.com/ref-3.png" },
        { "text": "参考图1的人物穿着图2的衣服,摆出图3的姿势" }
      ]
    }]
  },
  "parameters": {
    "n": 1,
    "size": "1024*1024"
  }
}
  • model:模型标识,可选 qwen-image-plusqwen-image-max 等,以 DashScope 控制台实际可用的模型名为准
  • input.messages[].content[]:多模态输入数组,可混排 imagetext
  • parameters.n:生成图片数量
  • parameters.size:输出尺寸,格式为 宽*高

响应结构

json 复制代码
{
  "output": {
    "choices": [{
      "finish_reason": "stop",
      "message": {
        "role": "assistant",
        "content": [{
          "image": "https://your-cdn.example.com/generated/xxx.png"
        }]
      }
    }]
  },
  "usage": {
    "image_count": 1
  },
  "request_id": "xxx-xxx-xxx"
}

图片结果路径:output.choices[0].message.content[0].image


.gitignore 配置

gitignore 复制代码
node_modules
dist
*.local          # ← 关键:排除所有 .env.local 文件

# Editor
.vscode/*
.idea
.DS_Store

*.local 这条规则确保 .env.local 永远不会被提交到 Git,从源头保护 API Key。


运行项目

bash 复制代码
# 开发模式(HMR 热更新)
npm run dev

# 生产构建
npm run build

# 预览生产构建
npm run preview

关键知识点总结

知识点 说明
Vite 环境变量 VITE_* 前缀的变量通过 import.meta.env 暴露给前端代码
.env.local 存放敏感配置,必须 加入 .gitignore
Vite 大管家 Vite 统一管理 HMR、ESM、CSS 处理、环境变量注入、构建优化
多模态 API DashScope 的 /multimodal-generation/generation 端点支持图文混合输入
ESM type="module" + Vite 实现原生 ES Module 开发体验
Defense in Depth 多层防护:.gitignore + 环境变量 + 后端代理(生产环境)

写在最后

这个 Demo 虽然小巧,但涵盖了现代前端工程化的几个关键实践:

  • Vite 作为构建工具的统一调度能力
  • 环境变量在前端的安全管理方式
  • 多模态 AI API 的调用范式
  • 完整的加载-成功-失败状态管理

把这些基础打牢,无论是接图像生成、语音识别还是大模型对话,前端侧的接入模式都是相通的

相关推荐
Coffeeee1 小时前
Codachi — 藏在 Claude Code 状态栏里的电子宠物
人工智能·程序员·claude
张某布响丸辣1 小时前
Spring AI 极简入门:Java 开发者快速上手 AI 开发
java·人工智能·spring·springai
Deepoch1 小时前
VLA多模态架构加持 采摘机器人实现精细化智能采收
人工智能·机器人·开发板·具身模型·deepoc·采摘
想要成为糕糕手1 小时前
从零到一:CSS 3D 旋转立方体完全指南
前端·css·canvas
疯狂的魔鬼1 小时前
多角色督办任务详情页:从权限矩阵到组件拆分的完整实现
前端·vue.js·架构
谁似人间西林客1 小时前
工业AI原生企业是什么?制造业智能化升级的新路径
大数据·人工智能·ai-native
段一凡-华北理工大学1 小时前
LangChain框架在高炉炼铁智能化领域的应用~系列文章09:工具调用Tool — 让AI学会操作高炉仪表盘
网络·人工智能·架构·langchain·高炉炼铁·高炉智能化·高炉智能体
工业胶粘剂技术1 小时前
K-1306双组份丙烯酸结构胶技术白皮书:TDS全参数解析、核壳增韧机理与高端制造选型指南
大数据·人工智能·制造
FL16238631291 小时前
国内快递面单识别检测数据集VOC+YOLO格式422张6类别
人工智能·yolo·机器学习