前端搭建项目基础架构,本质是:在写业务代码之前,把项目的地基打好 。
包括技术选型、目录结构、工程化配置、规范约束、请求封装、路由权限、状态管理、构建部署等。地基打得好,后面开发才快、稳、可维护。
一句话:
选型定方向,目录定边界,配置定规范,封装定效率,部署定上线。
下面以 Vue3 + Vite + TypeScript 为主,讲一套完整的搭建流程。
一、基础架构包含哪些内容
| 模块 | 作用 |
|---|---|
| 技术选型 | 框架、UI 库、工具库 |
| 项目初始化 | Vite 创建、依赖安装 |
| 目录结构 | 分层、分模块 |
| 代码规范 | ESLint、Prettier、Stylelint、Commitlint |
| 环境变量 | 开发、测试、生产 |
| 请求封装 | Axios、拦截器、错误处理 |
| 路由与权限 | 动态路由、菜单、按钮权限 |
| 状态管理 | Pinia 模块化 |
| 组件封装 | 公共组件、业务组件 |
| 工具函数 | utils、hooks |
| 样式方案 | 全局样式、主题、变量 |
| 构建优化 | 分包、压缩、CDN |
| 部署配置 | Nginx、Docker、CI/CD |
| 文档与协作 | README、Git 规范、分支模型 |
二、第一步:技术选型
通用后台管理系统
Vue3 + Vite + TypeScript + Vue Router + Pinia + Element Plus + Axios + Sass + ESLint + Prettier
移动端 H5
Vue3 + Vite + TypeScript + Vant + Axios + postcss-px-to-viewport
官网 / 内容站
Nuxt3 + Tailwind CSS + TypeScript
多端小程序
uni-app 或 Taro
React 技术栈
React + Vite + TypeScript + React Router + Zustand/Redux + Ant Design + Axios
选型原则:
- 团队熟悉
- 生态成熟
- 社区活跃
- 长期维护
- 适合业务场景
三、第二步:初始化项目
bash
npm create vite@latest my-admin -- --template vue-ts
cd my-admin
npm install
安装核心依赖:
bash
# 路由、状态、请求、UI
npm install vue-router@4 pinia axios element-plus
# 工具库
npm install @vueuse/core dayjs
# 开发依赖
npm install -D sass eslint prettier eslint-plugin-vue @typescript-eslint/eslint-plugin @typescript-eslint/parser
npm install -D unplugin-auto-import unplugin-vue-components
四、第三步:设计目录结构
text
src/
├── api/ # 接口定义
│ ├── user.ts
│ └── order.ts
├── assets/ # 静态资源
│ ├── images/
│ └── icons/
├── components/ # 公共组件
│ ├── BaseTable/
│ └── BaseDialog/
├── composables/ # 组合式函数
│ ├── usePagination.ts
│ └── useRequest.ts
├── directives/ # 自定义指令
│ └── permission.ts
├── layouts/ # 布局
│ ├── DefaultLayout.vue
│ └── BlankLayout.vue
├── router/ # 路由
│ ├── index.ts
│ ├── routes.ts
│ └── guard.ts
├── stores/ # Pinia
│ ├── user.ts
│ ├── permission.ts
│ └── app.ts
├── styles/ # 全局样式
│ ├── index.scss
│ ├── variables.scss
│ └── reset.scss
├── utils/ # 工具函数
│ ├── request.ts
│ ├── storage.ts
│ └── date.ts
├── views/ # 页面
│ ├── login/
│ ├── dashboard/
│ └── system/
├── App.vue
├── main.ts
└── env.d.ts
原则:
- 按职责分层
- 按业务分模块
- 公共和业务分离
- 页面和组件分离
五、第四步:配置入口和核心文件
1. main.ts
ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'
import './styles/index.scss'
import { setupDirectives } from './directives'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.use(ElementPlus)
setupDirectives(app)
app.mount('#app')
2. App.vue
vue
<template>
<router-view />
</template>
3. vite.config.ts
ts
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { fileURLToPath, URL } from 'node:url'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd())
return {
plugins: [
vue(),
AutoImport({ resolvers: [ElementPlusResolver()] }),
Components({ resolvers: [ElementPlusResolver()] })
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
port: 5173,
proxy: {
'/api': {
target: env.VITE_API_TARGET,
changeOrigin: true
}
}
},
build: {
rollupOptions: {
output: {
manualChunks: {
vue: ['vue', 'vue-router', 'pinia'],
element: ['element-plus']
}
}
}
}
}
})
六、第五步:环境变量
根目录创建:
.env.development
env
VITE_API_BASE_URL=/api
VITE_API_TARGET=http://localhost:3000
.env.production
env
VITE_API_BASE_URL=https://api.example.com
使用:
ts
import.meta.env.VITE_API_BASE_URL
七、第六步:请求封装
src/utils/request.ts
ts
import axios from 'axios'
import { storage } from './storage'
import { ElMessage } from 'element-plus'
const request = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000
})
request.interceptors.request.use(config => {
const token = storage.get<string>('token')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
request.interceptors.response.use(
res => {
const { code, message, data } = res.data
if (code === 0) return data
ElMessage.error(message || '请求失败')
return Promise.reject(new Error(message))
},
err => {
ElMessage.error(err.message || '网络错误')
return Promise.reject(err)
}
)
export default request
src/api/user.ts
ts
import request from '@/utils/request'
export const login = (data: { username: string; password: string }) =>
request.post('/login', data)
export const getUserInfo = () => request.get('/user/info')
八、第七步:路由与权限
src/router/index.ts
ts
import { createRouter, createWebHistory } from 'vue-router'
import { constantRoutes } from './routes'
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes
})
export default router
src/router/guard.ts
ts
import router from './index'
import { useUserStore } from '@/stores/user'
router.beforeEach(async (to) => {
const userStore = useUserStore()
if (to.path === '/login') return true
if (!userStore.token) {
return { path: '/login', query: { redirect: to.fullPath } }
}
if (!userStore.userInfo) {
await userStore.fetchUserInfo()
const routes = await userStore.generateRoutes()
routes.forEach(r => router.addRoute(r))
return { ...to, replace: true }
}
return true
})
权限三个层面:
- 菜单权限:动态生成侧边栏
- 按钮权限:自定义指令
v-permission - 接口权限:后端校验
src/directives/permission.ts
ts
import type { App } from 'vue'
import { useUserStore } from '@/stores/user'
export function setupDirectives(app: App) {
app.directive('permission', {
mounted(el, binding) {
const userStore = useUserStore()
const required = binding.value
const has = Array.isArray(required)
? required.some(p => userStore.permissions.includes(p))
: userStore.permissions.includes(required)
if (!has) el.parentNode?.removeChild(el)
}
})
}
九、第八步:状态管理 Pinia
src/stores/user.ts
ts
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { login, getUserInfo } from '@/api/user'
import { storage } from '@/utils/storage'
export const useUserStore = defineStore('user', () => {
const token = ref(storage.get<string>('token') || '')
const userInfo = ref<any>(null)
const permissions = ref<string[]>([])
async function doLogin(data: { username: string; password: string }) {
const res = await login(data)
token.value = res.token
storage.set('token', res.token)
}
async function fetchUserInfo() {
const res = await getUserInfo()
userInfo.value = res
permissions.value = res.permissions
}
function logout() {
token.value = ''
userInfo.value = null
permissions.value = []
storage.remove('token')
}
return { token, userInfo, permissions, doLogin, fetchUserInfo, logout }
})
十、第九步:代码规范
ESLint
.eslintrc.cjs
js
module.exports = {
root: true,
env: { browser: true, es2021: true, node: true },
extends: [
'eslint:recommended',
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended'
],
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 'latest',
sourceType: 'module'
},
rules: {
'vue/multi-word-component-names': 'off'
}
}
Prettier
.prettierrc
json
{
"semi": false,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "none"
}
Commitlint + Husky
bash
npm install -D husky lint-staged @commitlint/cli @commitlint/config-conventional
npx husky init
commit-msg:
bash
npx --no -- commitlint --edit $1
pre-commit:
bash
npx lint-staged
package.json:
json
{
"lint-staged": {
"*.{js,ts,vue}": ["eslint --fix", "prettier --write"],
"*.{scss,css}": ["stylelint --fix", "prettier --write"]
}
}
提交规范:
feat:新功能fix:修复docs:文档style:格式refactor:重构perf:性能test:测试chore:构建/工具
十一、第十步:样式方案
src/styles/variables.scss
scss
$primary-color: #409eff;
$header-height: 60px;
$sidebar-width: 220px;
src/styles/index.scss
scss
@use './reset.scss';
@use './variables.scss' as *;
:root {
--primary-color: #{$primary-color};
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
Vite 全局注入:
ts
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/variables.scss" as *;`
}
}
}
十二、第十一步:构建与部署
构建:
bash
npm run build
Nginx:
nginx
server {
listen 80;
root /var/www/my-admin/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:3000;
}
}
Docker:
dockerfile
FROM node:20-alpine AS build
WORKDIR /app
COPY . .
RUN npm install && npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
CI/CD:
- GitHub Actions
- GitLab CI
- Jenkins
十三、可选增强
- Mock:vite-plugin-mock、MSW
- 国际化:vue-i18n
- 主题切换:CSS 变量 + Pinia
- 错误监控:Sentry
- 埋点:自定义指令 + 上报
- 权限指令:v-permission
- 水印:Canvas 生成
- 大屏适配:scale 方案
- 微前端:qiankun、micro-app
- Monorepo:pnpm workspace + Turborepo
十四、搭建顺序建议
- 初始化 Vite 项目
- 配置别名、环境变量
- 安装 Router、Pinia、UI 库
- 设计目录结构
- 封装 request
- 配置路由和守卫
- 配置 Pinia 模块
- 配置 ESLint、Prettier、Husky
- 配置全局样式
- 封装公共组件和工具
- 配置构建优化
- 配置部署
十五、常见坑
- 一开始就上微前端、Monorepo,过度设计
- 目录混乱,业务和公共混在一起
- 不配 ESLint,代码风格失控
- 请求不封装,每处都写 axios
- 权限没设计好,后期返工
- 环境变量写死,部署要改代码
- 不配 CI/CD,每次手动部署
- 不做分包,首屏加载慢
十六、总结
前端项目基础架构的核心:
选型合理、目录清晰、规范统一、封装到位、部署顺畅。
一个合格的基础架构应该:
- 新人能快速上手
- 业务开发不用关心底层配置
- 代码风格统一
- 权限、请求、路由开箱即用
- 构建部署自动化
- 易扩展、易维护
先用成熟模板(如 Geeker-Admin、Vue Vben Admin)快速起步,再根据团队和业务逐步调整,是最高效的方式。
如果你告诉我具体项目类型(后台、H5、小程序),我可以给你一份更精确的基础架构清单和代码模板。