Claude Code 100个真实案例 - 用AI搭建微前端应用(qiankun+独立部署子应用)
1. 为什么我用 Claude Code 搭 qiankun 微前端而不是手写骨架微前端这个词听起来玄乎落到工程上其实就一句话把一个大的前端系统拆成几个能独立开发、独立部署的小应用再用一个主应用把它们拼起来。qiankun 是目前国内落地最多的方案之一主应用负责导航和容器子应用负责具体业务Vue3 和 React 可以混着用。适合谁适合手里有多个团队、多个技术栈、又不想推倒重来的项目。我这次要交付的东西很具体一个 Vue3 Vite 的主应用骨架两个子应用Vue3 一个、React 一个子应用能独立跑也能被主应用加载路由注册、跨应用通信、样式隔离都要能跑通。手写这套骨架光是把 qiankun 的生命周期、Vite 的 UMD 打包、路由前缀对齐就得折腾大半天还容易在__POWERED_BY_QIANKUN__这种细节上翻车。所以我用 Claude Code 来生成这套骨架。但这里有个前提Claude Code 需要一个稳定的模型调用通道。我这边统一走 TaoToken 的 API 通道Key 和 Base URL 配一次Claude Code、Cline、Codex 这些工具都能复用不用每个工具单独折腾一套鉴权。下面我会先把通道配好再让 Claude Code 生成主应用和子应用的完整代码最后在浏览器里验证主/子应用加载与切换。整篇的节奏是先配通道再生成主应用再生成两个子应用然后本地联调最后把常见报错挨个排掉。每一步都有可复制的配置和命令你跟着敲就行。2. TaoToken 前置给 Claude Code 配好统一 Key 与 API 通道Claude Code 默认走的是 Anthropic 官方通道国内直连经常超时。我的做法是把它指向 TaoToken 的 API 地址用一个 Key 统一管理。TaoToken 在这里的角色是模型调用的统一入口不是替代你的编辑器也不是什么灰色通道就是一个正常的 API 网关。先拿到 Key。打开 https://taotoken.net/api-keys 登录后创建一个 API Key复制出来。这个 Key 后面会写进 Claude Code 的配置里。Claude Code 的配置有两种方式一种是环境变量一种是配置文件。我推荐配置文件因为可以跟着项目走。在项目根目录或者用户目录下建一个.claude/settings.json内容如下{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_AUTH_TOKEN: sk-你的TaoToken密钥, ANTHROPIC_MODEL: claude-sonnet-4-20250514 } }这里三个字段要写全Base URL 是https://taotoken.net/apiKey 是你刚复制的Model ID 填你套餐里支持的模型。三个缺一个都会报鉴权或模型不存在的错。如果你用的是 Cline 或者 Codex配置逻辑一样只是文件位置不同。Cline 在 VS Code 的设置里填 Base URL 和 KeyCodex 走~/.codex/auth.json结构类似。核心就一句话Base URL Key Model ID 三件套缺一不可。配完之后验证一下通道通不通。在终端里跑curl https://taotoken.net/api/v1/messages \ -H x-api-key: sk-你的TaoToken密钥 \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d { model: claude-sonnet-4-20250514, max_tokens: 64, messages: [{role: user, content: ping}] }返回里能看到content字段就说明通道正常。如果返回 401多半是 Key 写错了或者带了多余空格如果返回模型不存在检查 Model ID 是不是你套餐里有的。这一步过了再进 Claude Code 就不会在鉴权上浪费时间。通道配好之后Claude Code 里执行/status能看到当前的 Base URL 和模型确认指向的是 TaoToken 而不是默认地址。这一步别省我见过太多人配了文件但没生效结果一直在用默认通道。3. 可复制配置主应用 qiankun 注册与子应用接入代码这一节是核心所有配置都能直接复制。先建目录结构mkdir micro-frontend cd micro-frontend mkdir main-app sub-vue sub-react主应用用 Vue3 Vitecd main-app npm create vitelatest . -- --template vue-ts npm install qiankun element-plus element-plus/icons-vue vue-router pinia主应用最关键的配置是 qiankun 的注册文件。新建main-app/src/micro/registerApps.tsimport { registerMicroApps, start, initGlobalState, type MicroAppStateActions } from qiankun const microApps [ { name: sub-vue, entry: //localhost:5174, container: #sub-app-container, activeRule: /sub-vue, props: { routerBase: /sub-vue } }, { name: sub-react, entry: //localhost:3001, container: #sub-app-container, activeRule: /sub-react, props: { routerBase: /sub-react } } ] const initialState { user: null as any, token: localStorage.getItem(token) || , theme: light } let stateActions: MicroAppStateActions export function initGlobalSharedState() { stateActions initGlobalState(initialState) stateActions.onGlobalStateChange((state) { console.log(全局状态变化:, state) }) return stateActions } export function setGlobalState(state: Partialtypeof initialState) { if (stateActions) { stateActions.setGlobalState(state) } } export function registerAndStart() { registerMicroApps(microApps, { beforeLoad: [async (app) console.log(加载子应用: ${app.name})], beforeMount: [async (app) console.log(挂载子应用: ${app.name})], afterMount: [async (app) console.log(子应用已挂载: ${app.name})], beforeUnmount: [async (app) console.log(卸载子应用: ${app.name})], afterUnmount: [async (app) console.log(子应用已卸载: ${app.name})] }) start({ sandbox: { strictStyleIsolation: false, experimentalStyleIsolation: true }, prefetch: all, singular: true }) console.log(qiankun 微前端已启动) }主应用的App.vue里要留一个容器#sub-app-container并在onMounted里调用initGlobalSharedState()和registerAndStart()。路由配置里给子应用留通配符{ path: /sub-vue/:pathMatch(.*)*, component: () import(./views/SubAppWrapper.vue) }, { path: /sub-react/:pathMatch(.*)*, component: () import(./views/SubAppWrapper.vue) }Vue3 子应用的 Vite 配置要加vite-plugin-qiankuncd ../sub-vue npm create vitelatest . -- --template vue-ts npm install vite-plugin-qiankun vue-router element-plussub-vue/vite.config.tsimport { defineConfig } from vite import vue from vitejs/plugin-vue import qiankun from vite-plugin-qiankun export default defineConfig({ plugins: [ vue(), qiankun(sub-vue, { useDevMode: true }) ], server: { port: 5174, cors: true, origin: http://localhost:5174 }, build: { target: esnext, lib: { entry: ./src/main.ts, name: sub-vue, formats: [umd] } } })子应用入口sub-vue/src/main.ts要导出 qiankun 生命周期import { createApp, type App as VueApp } from vue import { createRouter, createWebHistory, type Router } from vue-router import { renderWithQiankun, qiankunWindow, type QiankunProps } from vite-plugin-qiankun/dist/helper import ElementPlus from element-plus import element-plus/dist/index.css import AppComponent from ./App.vue import { routes } from ./router let app: VueApp | null null let router: Router | null null function render(props: QiankunProps { container?: Element }) { const { container } props const base qiankunWindow.__POWERED_BY_QIANKUN__ ? /sub-vue : / router createRouter({ history: createWebHistory(base), routes }) app createApp(AppComponent) app.use(router) app.use(ElementPlus) app.config.globalProperties.$mainAppProps props const mountEl container ? container.querySelector(#sub-vue-app) : document.getElementById(sub-vue-app) app.mount(mountEl || #sub-vue-app) } if (qiankunWindow.__POWERED_BY_QIANKUN__) { renderWithQiankun({ bootstrap() { console.log(sub-vue bootstrap) }, mount(props) { if (props.onGlobalStateChange) { props.onGlobalStateChange((state: any) { console.log(子应用收到全局状态:, state) }, true) } render(props) }, unmount() { if (app) { app.unmount(); app null } if (router) { router null } }, update(props) { console.log(sub-vue update:, props) } }) } else { render({} as any) }React 子应用走 CRAcd ../sub-react npx create-react-app . --template typescript npm install react-router-domsub-react/src/index.tsx导出生命周期import React from react import ReactDOM from react-dom/client import { BrowserRouter } from react-router-dom import App from ./App let root: ReactDOM.Root | null null function render(props: any {}) { const { container } props const mountNode container ? container.querySelector(#root) : document.getElementById(root) if (!mountNode) return const basename (window as any).__POWERED_BY_QIANKUN__ ? /sub-react : / root ReactDOM.createRoot(mountNode) root.render( React.StrictMode BrowserRouter basename{basename} App mainAppProps{props} / /BrowserRouter /React.StrictMode ) } export async function bootstrap() { console.log(sub-react bootstrap) } export async function mount(props: any) { if (props.onGlobalStateChange) { props.onGlobalStateChange((state: any) { console.log(React 子应用收到全局状态:, state) }, true) } render(props) } export async function unmount() { if (root) { root.unmount(); root null } } if (!(window as any).__POWERED_BY_QIANKUN__) { render() }这三份配置就是整套微前端的骨架。主应用注册、Vue3 子应用接入、React 子应用接入全部可复制。注意子应用的name必须和主应用注册的name一致端口不能冲突路由前缀要对齐这三点错了后面必翻车。4. 验证请求本地联调主/子应用加载与切换配置写完启动三个应用。开三个终端# 终端1主应用 cd main-app npm run dev # 输出Local: http://localhost:5173 # 终端2Vue3 子应用 cd sub-vue npm run dev # 输出Local: http://localhost:5174 # 终端3React 子应用 cd sub-react npm start # 输出Local: http://localhost:3001先单独验证子应用能独立跑。浏览器打开http://localhost:5174Vue3 子应用应该正常显示订单列表打开http://localhost:3001React 子应用显示个人资料页。这一步过了说明子应用本身没问题。再验证主应用加载子应用。打开http://localhost:5173主应用框架出来左侧导航有「订单管理 (Vue3)」和「用户中心 (React)」。点「订单管理」地址变成http://localhost:5173/sub-vue/list内容区加载出 Vue3 子应用的订单列表。点「用户中心」地址变成http://localhost:5173/sub-react/profile内容区切换成 React 子应用。打开浏览器控制台能看到生命周期日志按顺序打印加载子应用: sub-vue 挂载子应用: sub-vue 子应用已挂载: sub-vue 子应用收到全局状态: { user: {...}, token: mock-token, theme: light }切换应用时前一个子应用会打印卸载日志后一个打印挂载日志。全局状态在initGlobalSharedState之后通过setGlobalState推给子应用子应用的onGlobalStateChange能收到。这一步验证的是跨应用通信主应用改用户信息子应用能实时拿到。再验证样式隔离。主应用用了 Element Plus子应用也用了 Element Plus如果没开experimentalStyleIsolation两边的样式会互相污染。开了之后子应用的样式会被加上作用域前缀主应用的按钮样式不会影响子应用。你可以在主应用里改一个按钮颜色看子应用里的按钮有没有跟着变没变就说明隔离生效了。最后验证独立部署。子应用单独npm run build产物丢到静态服务器主应用的entry改成线上地址照样能加载。这就是「独立部署子应用」的意义子应用发版不用动主应用主应用发版不用管子应用。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth配这套东西报错基本集中在几个地方。我按真实遇到的顺序列出来。401 鉴权失败。这个多半出在 TaoToken 通道配置上。检查.claude/settings.json里的ANTHROPIC_AUTH_TOKEN是不是完整的 Key有没有多余空格或换行。Base URL 必须是https://taotoken.net/api结尾不要多加/v1Claude Code 会自己拼。如果用的是 Cline检查设置里的 API Key 字段Codex 检查~/.codex/auth.json里的字段名。三件套 Base URL Key Model ID 任何一个错了都会 401 或模型不存在。local proxy failed。这个报错通常出现在 Claude Code 启动时说明它尝试走本地代理但没连上。检查你的环境变量里有没有残留的HTTP_PROXY或HTTPS_PROXY有的话清掉。Claude Code 直连 TaoToken 的 API 地址就行不需要额外代理。如果公司网络有出口限制确认taotoken.net在允许列表里。reading choices 报错。这个一般出在模型返回格式解析上常见于 Model ID 填错或者套餐不支持该模型。回到.claude/settings.json把ANTHROPIC_MODEL换成你套餐里明确支持的模型 ID。如果用的是 Coding Plan去 https://taotoken.net/coding-plan 确认可用模型列表。填错模型 ID 时接口可能返回一个非预期结构Claude Code 解析choices字段就报错了。OAuth 相关报错。Claude Code 某些版本会尝试 OAuth 登录流程如果你已经用 API Key 配置了就不需要走 OAuth。检查配置里有没有同时存在 OAuth token 和 API Key两者冲突时会报错。清掉 OAuth 相关配置只保留ANTHROPIC_BASE_URL和ANTHROPIC_AUTH_TOKEN。子应用加载空白。主应用能出来但子应用区域空白打开控制台看有没有Application died in status LOADING_SOURCE_CODE。这通常是子应用入口没导出 qiankun 生命周期或者vite-plugin-qiankun的useDevMode没开。检查sub-vue/src/main.ts里renderWithQiankun的分支有没有走到qiankunWindow.__POWERED_BY_QIANKUN__在主应用加载时应该是true。路由 404。主应用切到/sub-vue/list刷新页面 404是因为主应用的 dev server 没配 history fallback。Vite 默认支持但如果你改了base或者用了自定义 server要确认 fallback 到index.html。子应用独立跑时刷新 404检查createWebHistory的 base 参数独立模式应该是/子应用模式才是/sub-vue。样式互相污染。主应用和子应用都用 Element Plus 时最容易出现。确认start里开了experimentalStyleIsolation: true。如果还不行检查子应用的样式是不是写在了全局qiankun 的作用域前缀对body这种根选择器无效子应用样式尽量写在组件内。6. 把通道和骨架固化下来下次直接复用这套东西跑通之后我建议你做两件事。第一把.claude/settings.json里的 TaoToken 配置抽成模板新项目直接复制Key 用环境变量注入别硬编码在文件里。第二把主应用的registerApps.ts和子应用的生命周期入口抽成脚手架下次加子应用只改microApps数组和端口。Claude Code 在这里的价值不是替你写业务代码而是把 qiankun 这套容易出错的骨架一次性生成对。生命周期、路由前缀、UMD 打包、样式隔离这些细节手写容易漏让模型按你的提示词生成再对照本文的配置检查一遍能省掉大量调试时间。通道方面TaoToken 的 API 地址是 https://taotoken.net/api Key 在 https://taotoken.net/api-keys 管理。如果你要长期跑编码和 Agent 任务Coding Plan 的入口在 https://taotoken.net/coding-plan 模型对话在 https://taotoken.net 接入文档在 https://taotoken.net/doc 。这几个地址按需用核心还是把 Base URL Key Model ID 三件套配对。最后留一个实操建议主应用和子应用的端口规划提前定好5173 主、5174 Vue3 子、3001 React 子别中途改。路由前缀和子应用name保持一致sub-vue对应/sub-vuesub-react对应/sub-react。这两条守住了qiankun 的坑能避开一大半。