|
| 1 | +--- |
| 2 | +create_date: 2026-03-31 |
| 3 | +last_date: 2026-03-31 |
| 4 | +--- |
| 5 | +# 个人实战 |
| 6 | + |
| 7 | +可参考: any-menu/ai-app-vue 项目 (这可能是个隐私项目) |
| 8 | + |
| 9 | +## 从零构建 - vue 版 |
| 10 | + |
| 11 | +见 https://ai-sdk.dev/docs/getting-started/nuxt |
| 12 | + |
| 13 | +后续均以 pnpm 为例,如果你想要用 npm / yarn / bun 请参照原文 |
| 14 | + |
| 15 | +(1) 创建普通的 nuxt vue 应用 |
| 16 | + |
| 17 | +> [!WARNING] |
| 18 | +> |
| 19 | +> 注意 Nuxt 4 和 3 在用法上有些区别。 |
| 20 | +> 我看文档时他教的是 3 的做法,但我 `pnpm create nuxt ...` 时创建的是 4 的模板,所以按他教的去做会存在问题 |
| 21 | +> |
| 22 | +> Nuxt 官网文档上的升级指南: https://nuxt.com.cn/docs/4.x/getting-started/upgrade/ |
| 23 | +
|
| 24 | +```bash |
| 25 | +$ pnpm create nuxt ai-app-vue |
| 26 | +Which template would you like to use? |
| 27 | +│ ● minimal – Minimal setup for Nuxt 4 (recommended) |
| 28 | +Which package manager would you like to use? |
| 29 | +│ ● pnpm (current) |
| 30 | +Initialize git repository? |
| 31 | +│ ● Yes |
| 32 | +Would you like to browse and install modules? |
| 33 | +│ ○ Yes / ● No |
| 34 | + |
| 35 | +$ cd ai-app-vue |
| 36 | +$ pnpm install |
| 37 | +$ pnpm dev # 或 pnpm run dev -- -o |
| 38 | +``` |
| 39 | + |
| 40 | +(2) 添加 vercal ai 依赖 |
| 41 | + |
| 42 | +```bash |
| 43 | +$ pnpm add ai @ai-sdk/vue zod |
| 44 | +$ touch .env |
| 45 | + |
| 46 | +# 编辑 .env 文件: (xxxxxxxxx 填你的实际 Vercel AI Gateway API 密钥) |
| 47 | +# NUXT_AI_GATEWAY_API_KEY=xxxxxxxxx |
| 48 | + |
| 49 | +# 编辑 nuxt.config.ts: |
| 50 | +# export default defineNuxtConfig({ |
| 51 | +# // rest of your nuxt config |
| 52 | +# runtimeConfig: { |
| 53 | +# aiGatewayApiKey: '', |
| 54 | +# }, |
| 55 | +# }); |
| 56 | +``` |
| 57 | + |
| 58 | +(3) 创建API路由 |
| 59 | + |
| 60 | +创建 API 路由 server/api/chat.ts 并添加以下代码: |
| 61 | + |
| 62 | +```ts |
| 63 | +import { |
| 64 | + streamText, |
| 65 | + UIMessage, |
| 66 | + convertToModelMessages, |
| 67 | + createGateway, |
| 68 | +} from 'ai'; |
| 69 | + |
| 70 | +export default defineLazyEventHandler(async () => { |
| 71 | + const apiKey = useRuntimeConfig().aiGatewayApiKey; |
| 72 | + if (!apiKey) throw new Error('Missing AI Gateway API key'); |
| 73 | + const gateway = createGateway({ |
| 74 | + apiKey: apiKey, |
| 75 | + }); |
| 76 | + |
| 77 | + return defineEventHandler(async (event: any) => { |
| 78 | + const { messages }: { messages: UIMessage[] } = await readBody(event); |
| 79 | + |
| 80 | + const result = streamText({ |
| 81 | + model: gateway('anthropic/claude-sonnet-4.5'), |
| 82 | + messages: await convertToModelMessages(messages), |
| 83 | + }); |
| 84 | + |
| 85 | + return result.toUIMessageStreamResponse(); |
| 86 | + }); |
| 87 | +}); |
| 88 | +``` |
| 89 | + |
| 90 | +(4) 连接UI |
| 91 | + |
| 92 | +原文档基于 Nuxt V3,说让你放 pages/index.vue |
| 93 | + |
| 94 | +> Nuxt V4 放 app/pages/index.vue,并在 app.vue 中添加 `<NuxtPage />` |
| 95 | +> |
| 96 | +> 参考: https://nuxt.com.cn/docs/4.x/getting-started/views |
| 97 | +
|
| 98 | +```vue |
| 99 | +<script setup lang="ts"> |
| 100 | +import { Chat } from "@ai-sdk/vue"; |
| 101 | +import { ref } from "vue"; |
| 102 | +
|
| 103 | +const input = ref(""); |
| 104 | +const chat = new Chat({}); |
| 105 | +
|
| 106 | +const handleSubmit = (e: Event) => { |
| 107 | + e.preventDefault(); |
| 108 | + chat.sendMessage({ text: input.value }); |
| 109 | + input.value = ""; |
| 110 | +}; |
| 111 | +</script> |
| 112 | +
|
| 113 | +<template> |
| 114 | + <div> |
| 115 | + <div v-for="(m, index) in chat.messages" :key="m.id ? m.id : index"> |
| 116 | + {{ m.role === "user" ? "User: " : "AI: " }} |
| 117 | + <div |
| 118 | + v-for="(part, index) in m.parts" |
| 119 | + :key="`${m.id}-${part.type}-${index}`" |
| 120 | + > |
| 121 | + <div v-if="part.type === 'text'">{{ part.text }}</div> |
| 122 | + </div> |
| 123 | + </div> |
| 124 | +
|
| 125 | + <form @submit="handleSubmit"> |
| 126 | + <input v-model="input" placeholder="Say something..." /> |
| 127 | + </form> |
| 128 | + </div> |
| 129 | +</template> |
| 130 | +``` |
| 131 | + |
| 132 | +### 原理 |
| 133 | + |
| 134 | +前端的 |
| 135 | + |
| 136 | +```ts |
| 137 | +import { Chat } from "@ai-sdk/vue"; |
| 138 | +chat.sendMessage(OBJ); |
| 139 | +``` |
| 140 | + |
| 141 | +会自动把消息发送给后端的 api 接口 |
| 142 | + |
| 143 | +然后后端的 |
| 144 | + |
| 145 | +```ts |
| 146 | +export default defineLazyEventHandler(async () => { ... }) |
| 147 | +``` |
| 148 | + |
| 149 | +定义后端接口 |
| 150 | + |
| 151 | +并且在该接口中可以从 |
| 152 | + |
| 153 | +```ts |
| 154 | +const { OBJ }: { OBJ_TYPE } = await readBody(event); |
| 155 | +``` |
| 156 | + |
| 157 | +中获得前端过来的数据,进行请求。并接返回结果通过返回前端 |
| 158 | + |
| 159 | +## 其他 - 切换 api |
| 160 | + |
| 161 | +例如我这里如果想要使用 deepseek: |
| 162 | + |
| 163 | +先找支持的提供商量: https://ai-sdk.dev/docs/foundations/providers-and-models |
| 164 | + |
| 165 | +就发现有: [DeepSeek Provider](https://ai-sdk.dev/providers/ai-sdk-providers/deepseek) (@ai-sdk/deepseek) |
| 166 | + |
| 167 | +然后按链接里的去做就可以了 |
| 168 | + |
| 169 | + |
| 170 | + |
| 171 | + |
0 commit comments