基于 Vue 的前端架构,我做了这 15 点:从 vue-router 到 Ant Design Vue 的工程化落地
发布时间:2026/9/27 12:26:47来源:尧图网络
1. 从零搭建 Vue 中大型项目我踩过的那些坑Vue 前端架构这件事说简单也简单vue create一把梭就能跑起来说复杂也复杂尤其是当项目从 3 个页面膨胀到 80 个页面、从 1 个人写到 6 个人协作的时候路由乱成一锅粥、vuex 状态到处飞、Ant Design Vue 组件样式互相打架这些问题会一个接一个冒出来。这篇内容聚焦的就是 Vue 中大型项目从零搭建到可维护的工程化路径覆盖 vue-router 路由分层、vuex 状态管理、Ant Design Vue 组件规范与目录结构设计最终交付一套可复制的 router 配置骨架、vuex module 拆分模板与目录约定并给出本地启动与构建验证动作。适合谁看如果你正在负责一个 GIS、BIM、后台管理这类中大型 Vue 系统或者团队要从零搭一个能撑住两三年迭代的前端底座那这篇内容里的 15 个点基本能覆盖你 80% 的决策场景。我试过把这套结构用在几个实际项目里后面招进来的新人基本两天就能上手改业务代码不用再问“这个接口封装在哪”“这个状态该放哪个 module”。下面按“问题场景 → 前置准备 → 可复制配置 → 验证 → 排障 → 工具入口”的顺序展开你可以直接照着搭。2. 先解决一个前置问题模型与 API 的统一入口在真正写 router 和 vuex 之前有个容易被忽略的前置环节项目里如果涉及 AI 能力比如智能表单填充、代码辅助、文档摘要你需要一个稳定的模型调用入口。很多团队的做法是每个人各自申请 key、各自写一套请求封装结果就是密钥散落、额度无法统一管理、换模型要改一堆代码。TaoToken 在这里扮演的角色就是统一入口它提供兼容 OpenAI 风格的 API你可以在一个控制台里管理密钥、查看用量、切换模型。对 Vue 项目来说你只需要在/libs/request.js里配一个 baseURL剩下的和普通接口没区别。具体入口我列一下方便你按需取用官网了解整体能力https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 接入地址写进环境变量https://taotoken.net/api模型对话体验验证模型是否可用https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite长期编码 / Agent 场景Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite控制台管理密钥与用量https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite创建 API Keyshttps://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewriteClaudeCode / Anthropic 兼容说明https://taotoken.net/claudecode-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite注意API 地址不要加 UTM 参数直接写https://taotoken.net/api即可否则部分 SDK 会把它当成非法路径。拿到 key 之后在项目根目录建.env.development和.env.production分别写入# .env.development VUE_APP_BASE_URL/api VUE_APP_AI_BASE_URLhttps://taotoken.net/api VUE_APP_AI_KEYsk-你的开发key VUE_APP_STATIC_URLhttp://localhost:8081/static # .env.production VUE_APP_BASE_URLhttps://your-domain.com/api VUE_APP_AI_BASE_URLhttps://taotoken.net/api VUE_APP_AI_KEYsk-你的生产key VUE_APP_STATIC_URLhttps://your-oss.aliyuncs.com/static这样做的价值在于AI 请求和业务请求走同一套 axios 封装拦截器、错误处理、loading 全部复用不用为 AI 单独维护一套请求逻辑。3. 可复制配置router 分层 vuex module 拆分 目录约定3.1 vue-router 路由分层骨架中大型项目最怕的就是router/index.js一个文件写 800 行。我的做法是按“布局层 → 业务模块层 → 页面层”三层拆src/router/ ├── index.js # 只做 createRouter 和全局守卫挂载 ├── routes/ │ ├── frameIn.js # 需要登录/权限的路由 │ ├── frameOut.js # 登录页、注册页等 │ └── errorPage.js # 404 / 403 └── guard/ ├── auth.js # 登录态校验 └── permission.js # 按钮级权限index.js保持极简import { createRouter, createWebHistory } from vue-router import frameIn from ./routes/frameIn import frameOut from ./routes/frameOut import errorPage from ./routes/errorPage import setupGuard from ./guard const router createRouter({ history: createWebHistory(process.env.BASE_URL), routes: [...frameOut, ...frameIn, ...errorPage], scrollBehavior: () ({ top: 0 }) }) setupGuard(router) export default routerframeIn.js里每个路由都带meta.auth和meta.permissionsexport default [ { path: /, component: () import(/layouts/BasicLayout.vue), redirect: /home, children: [ { path: home, name: Home, component: () import(/* webpackChunkName: home */ /views/home/index.vue), meta: { title: 首页, auth: true } }, { path: project, name: Project, component: () import(/* webpackChunkName: project */ /views/project/index.vue), meta: { title: 项目管理, auth: true, permissions: { add: true, delete: false } } } ] } ]webpackChunkName这条注释一定要加否则打包后你根本不知道哪个 chunk 对应哪个页面排查体积问题时会很痛苦。3.2 vuex module 拆分模板vuex 的拆分原则是“按页面模块划分默认 namespaced”。目录结构src/store/ ├── index.js ├── modules/ │ ├── system/ │ │ ├── user.js # 用户信息、token │ │ ├── menu.js # 菜单与路由 │ │ └── index.js │ └── project/ │ ├── list.js │ └── index.js └── plugins/ └── persist.js # 持久化插件单个 module 模板以user.js为例import { login, getInfo, logout } from /api/user import storage from store import { ACCESS_TOKEN } from /store/mutation-types const state { token: storage.get(ACCESS_TOKEN) || , info: {}, roles: [] } const getters { isLogin: state !!state.token, userName: state state.info.name || } const mutations { SET_TOKEN(state, token) { state.token token storage.set(ACCESS_TOKEN, token, 7 * 24 * 60 * 60 * 1000) }, SET_INFO(state, info) { state.info info }, CLEAR_ALL(state) { state.token state.info {} state.roles [] storage.remove(ACCESS_TOKEN) } } const actions { async Login({ commit }, payload) { const res await login(payload) commit(SET_TOKEN, res.token) return res }, async GetInfo({ commit }) { const res await getInfo() commit(SET_INFO, res) return res }, async Logout({ commit }) { await logout() commit(CLEAR_ALL) } } export default { namespaced: true, state, getters, mutations, actions }index.js里统一注册import { createStore } from vuex import user from ./modules/system/user import menu from ./modules/system/menu import projectList from ./modules/project/list export default createStore({ modules: { system/user: user, system/menu: menu, project/list: projectList } })命名用“大模块/子模块”的路径式 key调用时store.dispatch(system/user/GetInfo)一眼就知道数据归属比扁平命名清晰得多。3.3 Ant Design Vue 组件规范与目录约定Ant Design Vue 的样式覆盖是个老话题。我的建议是能用 JS 对象改内置变量就别用 less 覆盖 class因为后者会把全量样式打进来CSS 体积轻松破 500KB。// src/styles/antdTheme.js module.exports { primary-color: #1890ff, border-radius-base: 4px, font-size-base: 14px }// vue.config.js const modifyVars require(./src/styles/antdTheme) module.exports { css: { loaderOptions: { less: { lessOptions: { javascriptEnabled: true, modifyVars } } } } }配合babel-plugin-import做按需加载// babel.config.js module.exports { presets: [vue/cli-plugin-babel/preset], plugins: [ [import, { libraryName: ant-design-vue, libraryDirectory: es, style: true }] ] }目录约定我固定成这样团队里谁进来都按这个放src/ ├── api/ # 接口定义按模块分文件 ├── assets/ # 静态资源icons、images ├── components/ # 全局通用组件 ├── layouts/ # 布局组件 ├── libs/ # 工具库request、utils、storage ├── mock/ # mock 数据 ├── router/ # 路由 ├── store/ # vuex ├── styles/ # 全局样式 └── views/ # 页面按路由层级建文件夹views下的页面组件复杂页面在文件夹内再建components子目录页面入口统一叫index.vue。4. 验证请求本地启动与构建检查配置写完之后必须做两件事验证本地能跑、构建能过。本地启动npm run serve打开http://localhost:8080检查三件事路由跳转是否正常/home能加载未登录访问/project是否被重定向到登录页打开控制台 Network看 AI 请求是否打到https://taotoken.net/api返回 200vuex 里system/user/info在登录后是否有值。构建验证npm run build构建完成后看dist目录重点检查CSS 文件是否只有一个主 chunk 加若干异步 chunk主 CSS 是否控制在 200KB 以内每个页面是否生成了独立的 JS chunkchunk 名是否和webpackChunkName对应有没有出现moment的多语言包被打进来如果打进来了说明 ContextReplacementPlugin 没生效。如果构建体积异常装个分析工具npm install webpack-bundle-analyzer --save-dev// vue.config.js const BundleAnalyzerPlugin require(webpack-bundle-analyzer).BundleAnalyzerPlugin module.exports { chainWebpack: config { if (process.env.use_analyzer) { config.plugin(webpack-bundle-analyzer).use(BundleAnalyzerPlugin) } } }然后use_analyzertrue npm run build浏览器会自动打开体积分布图。5. 本篇常见错排查5.1 路由守卫死循环现象登录后一直跳登录页或者页面无限刷新。原因通常是beforeEach里对登录页本身也做了 auth 校验。正确写法是先判断to.path /user/login直接放行router.beforeEach(async (to, from, next) { NProgress.start() if (to.path /user/login) { next() return } if (to.matched.some(r r.meta.auth)) { const token storage.get(ACCESS_TOKEN) if (token) { if (Object.keys(store.state[system/user].info).length 0) { await store.dispatch(system/user/GetInfo) } next() } else { next({ name: Login, query: { redirect: to.fullPath } }) } } else { next() } })5.2 vuex module 命名空间找不到报错[vuex] unknown action type: system/user/GetInfo。检查两点module 导出时有没有写namespaced: true注册时 key 是不是和调用时一致。我见过有人注册写user调用写system/user/GetInfo自然找不到。5.3 Ant Design Vue 样式不生效按需加载开启后modifyVars有时不生效。原因是babel-plugin-import的style: true会引入 less 源文件而modifyVars需要在 less-loader 里配置。两者要同时存在缺一不可。5.4 AI 请求 401如果 AI 接口返回 401先检查.env里的 key 有没有被VUE_APP_前缀正确暴露。Vue CLI 只注入以VUE_APP_开头的变量写成AI_KEY是读不到的。另外确认请求头是Authorization: Bearer sk-xxx不是token。5.5 构建后静态资源 404检查VUE_APP_STATIC_URL在 production 环境是否指向了正确的 OSS 地址以及publicPath是否配置正确。如果是部署在子路径下vue.config.js里要设publicPath: /your-sub-path/。6. 工具入口与后续动作搭完这套骨架之后如果你还要继续做 AI 相关的功能接入建议按场景分流只是验证模型能不能用、效果如何直接去模型对话页试https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite要在项目里正式接入 API先去创建密钥https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite接入过程中遇到参数、鉴权、返回格式问题查接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite如果是长期做编码辅助、Agent 类功能看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite需要管理多个项目的密钥和用量进控制台https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite最后补一个实际经验这套架构里最容易被忽视的是libs/request.js的错误处理。我见过太多项目把 401、403、500 的处理散落在各个页面里结果 token 过期时用户看到的是白屏而不是登录页。把错误处理统一收口到拦截器配合 vuex 的CLEAR_ALL才是真正省心的做法。
网站建设高端定制企业官网