前端怎么搭建项目基础架构

前端搭建项目基础架构,本质是:在写业务代码之前,把项目的地基打好 。

包括技术选型、目录结构、工程化配置、规范约束、请求封装、路由权限、状态管理、构建部署等。地基打得好,后面开发才快、稳、可维护。

一句话:

选型定方向,目录定边界,配置定规范,封装定效率,部署定上线。

下面以 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

十四、搭建顺序建议

  1. 初始化 Vite 项目
  2. 配置别名、环境变量
  3. 安装 Router、Pinia、UI 库
  4. 设计目录结构
  5. 封装 request
  6. 配置路由和守卫
  7. 配置 Pinia 模块
  8. 配置 ESLint、Prettier、Husky
  9. 配置全局样式
  10. 封装公共组件和工具
  11. 配置构建优化
  12. 配置部署

十五、常见坑

  • 一开始就上微前端、Monorepo,过度设计
  • 目录混乱,业务和公共混在一起
  • 不配 ESLint,代码风格失控
  • 请求不封装,每处都写 axios
  • 权限没设计好,后期返工
  • 环境变量写死,部署要改代码
  • 不配 CI/CD,每次手动部署
  • 不做分包,首屏加载慢

十六、总结

前端项目基础架构的核心:

选型合理、目录清晰、规范统一、封装到位、部署顺畅。

一个合格的基础架构应该:

  • 新人能快速上手
  • 业务开发不用关心底层配置
  • 代码风格统一
  • 权限、请求、路由开箱即用
  • 构建部署自动化
  • 易扩展、易维护

先用成熟模板(如 Geeker-Admin、Vue Vben Admin)快速起步,再根据团队和业务逐步调整,是最高效的方式。

如果你告诉我具体项目类型(后台、H5、小程序),我可以给你一份更精确的基础架构清单和代码模板。

相关推荐
孟健1 小时前
Claude Code 同需求写两版对比:装不装 frontend-design 差多远?
前端·ai编程·claude
梦de天空一米16883 小时前
【一个重复id让游戏卡在62%,聊聊配置表检查】
前端
ryn483983 小时前
01 一个功能模块,怎么分层才不乱
前端·ai编程·claude
Amos_Web3 小时前
通过 Rspack 源码学 Rust(五):读懂 Result 与问号运算符的错误返回
前端
杨航 AI4 小时前
JEV的用法
java·前端·数据库
Amos_Web4 小时前
Rspack 源码解析(三十五):rspack_error 如何用 miette 渲染带源码定位的错误链
前端
去伪存真4 小时前
高敏资产场景下的大模型协同设计架构
前端·agent
修炼的dance4 小时前
OCR预处理测了108组,纠斜去噪有用,坑在顺序和力度
前端
疯华整猫4 小时前
Android 16 后台常驻适配:用 Trae 搭一套“前台服务 + 加急 Job”的可存活架构
前端
不可能片场4 小时前
AI视频成片 帧间差分把拼接缝照出来
前端·electron