企业级后台实战(上):从零搭建核心骨架与标准 CRUD

经过前面 15 篇文章的逐步铺垫,你手里已经集齐了现代 Vue 后台开发的核心装备:HTML/CSS/JS、Vite、Vue 3、Axios、Vue Router、Pinia、Element Plus、权限系统与 TypeScript。

但如果知识只停留在单个 Demo 里,很快就会遗忘。真实职场里,Vue 工程师最常接触的场景之一,就是中后台管理系统

这类项目并不追求炫酷动画,而更强调:

  • 页面骨架是否清晰
  • 路由和菜单是否规范
  • 登录态是否稳定
  • 网络层是否可维护
  • 列表、表单、弹窗等业务是否可复用

这篇文章就把前面学过的能力串起来,带你理解一个企业级后台管理系统从 0 到 1 的核心搭建过程。

1. 项目初始化与目录规范

1.1 创建项目

使用 Vite 快速初始化一个 Vue 项目:

npm create vite@latest my-admin-app -- --template vue
cd my-admin-app
npm install
npm install vue-router pinia element-plus axios

如果你准备继续走企业级路线,也建议尽早补上 nprogress

npm install nprogress
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'

router.beforeEach((to, from, next) => {
  // 路由切换开始时开启顶部进度条
  NProgress.start()
  next()
})

router.afterEach(() => {
  // 路由切换完成时关闭进度条
  NProgress.done()
})

💡 为什么需要 nprogress? 在单页应用(SPA)中,页面跳转是通过前端路由完成的,浏览器不会显示原生的加载圈。如果页面组件较大或需要请求权限,跳转会有延迟。nprogress 是一个轻量级的顶部进度条,配合路由守卫使用,能给用户明确的加载反馈,极大地提升系统的“专业感”。

1.2 为什么中后台项目特别强调目录规范

后台项目通常页面多、模块多、协作人数多。如果目录规划混乱,后续维护成本会急剧上升。

一个比较常见的结构如下:

src/
  ├── api/              # 接口请求
  ├── assets/           # 图片、字体、静态资源
  ├── components/       # 通用组件
  ├── layout/           # 整体布局
  ├── router/           # 路由配置与守卫
  ├── stores/           # Pinia 状态管理
  ├── styles/           # 全局样式
  ├── utils/            # 工具函数
  ├── views/            # 页面级组件
  ├── App.vue
  └── main.ts

1.3 目录设计背后的关键思想

这不是“为了看起来专业”,而是为了让职责更清晰:

  • api/ 只负责请求
  • stores/ 只负责状态
  • router/ 只负责导航与权限
  • layout/ 只负责页面骨架
  • views/ 只负责业务页面

你越早形成这种拆分意识,后面做大型项目越轻松。


2. 核心骨架:Layout 是后台项目的门面

后台系统最典型的结构就是:

  • 左侧菜单
  • 顶部导航
  • 中间内容区

这类结构应该抽成一个统一的 Layout 组件,而不是在每个页面里重复写。

2.1 一个典型的 Layout 结构

<template>
  <el-container class="app-wrapper">
    <el-aside width="220px" class="sidebar-container">
      <div class="logo">My Admin</div>
      <SidebarMenu />
    </el-aside>

    <el-container>
      <el-header class="header-container">
        <Navbar />
      </el-header>

      <el-main class="main-container">
        <router-view />
      </el-main>
    </el-container>
  </el-container>
</template>

2.2 为什么 Layout 要尽早抽出来

因为在后台项目里,很多页面只是“内容区不同”,外层骨架几乎不变。

提前抽出 Layout 的好处有:

  • 菜单、面包屑、顶部用户区都能统一维护
  • 页面切换逻辑更清晰
  • 后续接入标签页、缓存、权限控制更方便

这属于中后台项目最常见的基础架构知识点


3. 路由设计:菜单、页面、面包屑都依赖它

很多人第一次做后台时,会把路由只当成“页面跳转工具”。但在真实项目里,路由还承担很多职责:

  • 定义页面入口
  • 驱动侧边栏菜单
  • 生成面包屑
  • 标记页面是否需要登录
  • 标记页面是否需要缓存

3.1 推荐的路由 Meta 设计

interface RouteMeta {
  title?: string
  icon?: string
  hidden?: boolean
  requiresAuth?: boolean
  keepAlive?: boolean
}

例如:

import Layout from '@/layout/index.vue'

export const routes = [
  {
    path: '/login',
    component: () => import('@/views/login/index.vue'),
    meta: { hidden: true }
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    meta: { requiresAuth: true },
    children: [
      {
        path: 'dashboard',
        name: 'Dashboard',
        component: () => import('@/views/dashboard/index.vue'),
        meta: {
          title: '工作台',
          icon: 'Odometer',
          keepAlive: true
        }
      },
      {
        path: 'users',
        name: 'UserList',
        component: () => import('@/views/users/index.vue'),
        meta: {
          title: '用户管理',
          icon: 'User'
        }
      }
    ]
  }
]

3.2 为什么要给路由加 meta

因为中后台不是只有“能跳转”就够了,你还需要:

  • 菜单显示标题
  • 高亮当前菜单
  • 面包屑展示当前路径
  • 控制页面访问权限
  • 决定哪些页面进入缓存

这就是路由在后台系统里的“工程化价值”。


4. 登录闭环:网络、状态、路由三者协同

登录功能看上去只是一个表单,但它实际上会串起整个后台系统的主干能力。

4.1 登录闭环的完整过程

一个标准流程通常是:

  1. 用户输入账号密码
  2. 调用登录接口
  3. 拿到 Token
  4. 保存到 Pinia 和本地缓存
  5. 跳转到首页
  6. 路由守卫判断已登录,允许访问
  7. Axios 请求拦截器为后续请求自动挂载 Token

4.2 一个典型的登录页面逻辑

import { reactive } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { login } from '@/api/user'
import { useUserStore } from '@/stores/user'

const router = useRouter()
const userStore = useUserStore()

// 登录表单数据
const form = reactive({
  username: '',
  password: ''
})

const handleLogin = async () => {
  // 1. 发送登录请求
  const res = await login(form)
  // 2. 保存 token 到全局状态
  userStore.setToken(res.token)
  ElMessage.success('登录成功')
  // 3. 登录成功后进入首页
  router.push('/')
}

4.3 登录这件事真正的关键点

最重要的不是“请求能不能发出去”,而是:

  • Token 是否能稳定持久化
  • 刷新页面后是否还能恢复登录态
  • 退出登录时是否能清理干净

这些才是后台项目中最常踩坑的地方。


5. 路由守卫:后台系统的闸门

如果用户没登录,却能直接访问 /users/dashboard,那这个系统就不完整。

5.1 最基本的权限拦截

router.beforeEach((to, from, next) => {
  // 每次路由跳转前都先读取本地 token
  const token = localStorage.getItem('token')

  if (to.path === '/login') {
    next()
    return
  }

  if (!token) {
    next('/login')
    return
  }

  next()
})

5.2 企业项目里通常还会加什么

除了登录校验,很多项目还会在守卫中加入:

  • NProgress 顶部加载条
  • 动态修改页面标题
  • 权限路由加载
  • 用户信息初始化

例如:

import NProgress from 'nprogress'
import 'nprogress/nprogress.css'

router.beforeEach((to, from, next) => {
  // 路由切换开始时开启顶部进度条
  NProgress.start()
  document.title = to.meta.title ? `${to.meta.title} - My Admin` : 'My Admin'
  next()
})

router.afterEach(() => {
  NProgress.done()
})

这类细节不复杂,却能明显提升项目的“专业感”。


6. 侧边栏菜单:不能写死,必须跟路由联动

后台系统里,左侧菜单绝对不能只靠手写静态数组长期维护。

更推荐的做法是:

让菜单尽量基于路由表生成。

6.1 菜单渲染示例

<template>
  <el-menu :default-active="$route.path" router>
    <template v-for="route in menuRoutes" :key="route.path">
      <el-menu-item
        v-if="route.meta?.title && !route.meta?.hidden"
        :index="`/${route.path}`"
      >
        <span>{{ route.meta.title }}</span>
      </el-menu-item>
    </template>
  </el-menu>
</template>
const menuRoutes = computed(() => {
  // 从根路由的 children 中提取侧边栏菜单
  const rootRoute = router.options.routes.find((item) => item.path === '/')
  return rootRoute?.children || []
})

6.2 为什么菜单要跟路由绑定

这样做的好处非常明显:

  • 菜单和页面入口来源统一
  • 新增页面时不需要额外维护多套配置
  • 后期接权限系统时扩展更顺

这也是后面做“动态菜单”和“权限菜单”的基础。


7. 请求层:Axios 封装不是可选项,而是标配

后台项目的接口非常多,如果每个页面都手写完整请求逻辑,很快就会乱。

7.1 为什么要封装请求层

统一封装的价值包括:

  • 统一 baseURL
  • 统一超时设置
  • 统一错误处理
  • 统一 Token 注入
  • 统一响应数据格式

7.2 一个典型的请求封装思路

import axios from 'axios'

// 创建统一请求实例
const service = axios.create({
  baseURL: '/api',
  timeout: 10000
})

service.interceptors.request.use((config) => {
  // 请求发出前统一注入 token
  const token = localStorage.getItem('token')

  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }

  return config
})

service.interceptors.response.use((response) => {
  // 只把真正的业务数据返回给调用方
  return response.data
})

export default service

7.3 这一步最常见的实际价值

一旦后端接口变多,请求封装会让你:

  • 不必每次都重复写请求头
  • 不必每次都自己拆 response.data
  • 错误处理可以集中管理

这就是“Demo”和“项目”的区别。


8. 标准 CRUD 页面:后台项目的核心业务形态

在中后台系统里,最常见的页面就是 CRUD:

  • 列表查询
  • 条件筛选
  • 新增
  • 编辑
  • 删除

8.1 一页标准的用户管理界面通常有哪些部分

  • 查询表单
  • 操作按钮区
  • 数据表格
  • 分页器
  • 新增/编辑弹窗

8.2 一个典型的数据加载流程

import { onMounted, reactive, ref } from 'vue'
import { getUserList } from '@/api/user'

const loading = ref(false)
const tableData = ref([])
const total = ref(0)

// 列表查询参数
const queryParams = reactive({
  keyword: '',
  pageNum: 1,
  pageSize: 10
})

const fetchData = async () => {
  loading.value = true
  // 根据当前筛选条件拉取列表
  const res = await getUserList(queryParams)
  tableData.value = res.list
  total.value = res.total
  loading.value = false
}

onMounted(() => {
  fetchData()
})

8.3 CRUD 页面里常用但容易忽略的细节

  • 搜索后通常要把分页重置回第一页
  • 删除成功后要重新加载列表
  • 弹窗关闭时要清空表单
  • 表格 loading 状态要和请求绑定
  • 分页参数要和接口字段保持一致

这些细节看起来小,但往往决定页面体验是否专业。


9. 组件拆分:不要把一个页面写成上千行

后台页面非常容易越写越大,尤其是表格和表单混合页面。

9.1 推荐的拆分思路

例如“用户管理页”可以拆成:

views/users/
  ├── index.vue           # 页面容器
  ├── components/
  │   ├── SearchForm.vue
  │   ├── UserTable.vue
  │   └── UserDialog.vue

这样拆分的好处:

  • 搜索区域、表格区域、弹窗区域职责清晰
  • 更便于后续维护和复用
  • 页面主文件更容易阅读

9.2 什么逻辑适合继续抽成 composable

如果某些逻辑在多个页面都会复用,可以进一步抽成 useXxx()

  • 分页逻辑
  • 弹窗开关逻辑
  • 表格加载逻辑
  • 权限判断逻辑

这是从“会写页面”走向“会组织项目”的关键一步。


10. 状态持久化与页面缓存

后台系统里还有两个很高频的工程点:

  • 状态持久化
  • 页面缓存

10.1 状态持久化

像用户 Token、用户信息、主题配置这类数据,往往需要在刷新后保留。

最简单的方式是:

  • 用 Pinia 管理响应式状态
  • 再配合 localStorage 做持久化

10.2 页面缓存

对于列表页,如果用户从详情页返回时还想保留筛选条件和滚动位置,可以考虑配合 keep-alive

<router-view v-slot="{ Component }">
  <keep-alive :include="['UserList']">
    <component :is="Component" />
  </keep-alive>
</router-view>

当然,缓存并不是越多越好。缓存过多会带来状态混乱和内存负担,所以要结合业务场景使用。


11. 常用知识点与关键知识点总结

11.1 常用知识点

这些是你做后台项目几乎一定会遇到的:

  • 标准目录结构
  • 全局 Layout
  • 路由配置与嵌套路由
  • 路由守卫
  • Pinia 管理用户状态
  • Axios 请求封装
  • Element Plus 表格、表单、弹窗
  • CRUD 页面基本模式

11.2 关键知识点

这些决定你做出来的是不是“企业项目骨架”:

  • 路由不只是跳转工具,也是菜单和权限的基础
  • 登录功能必须和状态管理、守卫、请求层联动
  • 菜单最好跟路由表统一管理
  • 页面要及时拆分组件,避免主文件失控
  • 列表页、表单页、弹窗页要形成可复用模式

掌握这些,你的后台项目就不再只是一个散装 Demo,而会越来越接近真正的业务系统。

12. 结语

到这里,你已经具备了搭建一个 Vue 3 后台管理系统骨架所需的核心认知。

这篇文章最重要的,不是让你抄一份完整代码,而是让你理解一个真实后台项目背后的组织方式:

  • 页面如何布局
  • 路由如何设计
  • 登录如何闭环
  • 请求如何统一
  • 页面如何拆分

在真实职场中,后台项目的下一个高频专题通常就是:权限系统、动态路由、按钮级权限控制

目前我们的菜单是写死的,所有人看到的都一样。在下一篇《企业级后台实战(下):Vue 3 权限系统与动态路由深度解析》中,我们将引入 RBAC 模型,对现在的静态路由进行改造,实现真正的动态菜单和按钮级权限。