Vue3 + Element Plus + Vite 从零搭建管理后台原型完整指南
2026/9/20 17:16:00 网站建设 项目流程

先提个醒:管理后台这类项目,真正卡人的地方从来不是代码写不出来,而是从零搭建环境、配路由、接菜单、调样式这一整套流程太琐碎。网上模板倒是多,可每套模板都有自己的目录习惯和封装黑话,你光是搞清楚它把请求封装在哪、权限判断写在哪,就够折腾半天。与其到处找模板,不如花半天时间自己搭一个能跑起来的管理后台原型,底子是你自己的,后面想怎么改都顺手。

这篇文章我就用 Vue3 + Element Plus + Vite 这套组合,完整走一遍从初始化到出原型的流程。内容包括数据表格页、表单页、登录页、侧边菜单和顶部栏这些管理后台的标配模块。目标是让你照着做完,浏览器里能出来一个干净、可点击、能跳转的后台界面,而不是一堆躺在文件夹里的源码。

再说下适用人群。如果你已经会点 Vue 基础(知道组件、路由大概是什么东西),但没完整搭过后台项目,这篇文章刚好能带你顺一遍。如果你是完全零基础,也没关系,每一步我都会说清楚在干什么、为什么这么干,你照着敲也能跑起来。

1. 项目整体设计与搭建思路

1.1 为什么用 Vue3 + Element Plus,而不是其他组合

现在做中后台前端,Vue3 基本是绕不开的选择。相比 Vue2,Vue3 的组合式 API(Composition API)在逻辑复用上舒服很多。以前写 mixin 要小心命名冲突,现在一个useUserStore、一个useTableList就能把逻辑拆干净。再加上 Vite 的开发服务器启动速度确实快,改代码热更新基本是秒级响应,这对原型阶段频繁调样式、改布局来说非常关键。

Element Plus 是 Element UI 的 Vue3 版本,组件库本身的完整度在国内中后台场景里算是最高的。表格、表单、弹窗、日期选择器这些后台高频组件都开箱即用,默认样式也够干净,不需要像用 Naive UI 那样再搭一套主题变量。另外 Element Plus 的文档写得比较细,遇到问题搜索时中文资料也特别多,对新手来说踩坑成本低。

可能有人会问,为什么不直接用若依或 RuoYi 这类现成的后台脚手架?我的看法是:脚手架适合生产项目快速启动,但如果你是在做原型验证,或者想搞懂后台项目的结构逻辑,从零搭一遍反而更划算。因为你只有把路由配置、导航菜单、布局组件这些串联逻辑亲手写一遍,后面接真实接口、加权限控制时,才不会被脚手架里那层封装搞晕。

1.2 原型阶段需要一个什么量级的项目结构

原型阶段的目标是"能跑起来给需求方看效果"或者"给自己验证交互流程",所以项目结构不需要搞那么重,但基础分层还是要有,不然写到后面自己都找不到文件。

我建议按下面这个结构来:

vue-admin-proto ├── index.html ├── package.json ├── vite.config.js ├── src │ ├── main.js │ ├── App.vue │ ├── router │ │ ├── index.js │ │ └── routes.js │ ├── layout │ │ └── AdminLayout.vue │ ├── views │ │ ├── Login.vue │ │ ├── Dashboard.vue │ │ ├── UserList.vue │ │ └── ArticleEdit.vue │ └── api │ └── mock.js

router单独拆出来是因为后台的菜单和路由往往要做联动,后面你往里面加动态路由会方便。api目录放 mock 数据,原型阶段不用接真实后端,用 Promise 模拟异步请求就够了。layout目录放后台的整体框架组件,也就是侧边栏、顶栏、内容区这三块套在一起的壳。

1.3 版本选择与踩坑预防

在动手之前,先确认本机的 Node.js 版本。Vite 5 要求 Node 18+,Vite 6 要求更高。如果本机 Node 版本太低,建议先用nvm装一个 Node 18 或 20。这一步要是忽略,后面npm create vite可能直接报错,或者装完依赖启动不了。

Element Plus 的安装命令是:

npm install element-plus

这里有一个常见的问题:Element Plus 的按需自动导入需要用unplugin-auto-importunplugin-vue-components这两个插件。原型阶段我建议不要一上来就用按需导入,先把组件全量引入跑通再说。全量引入的代码量也就多几行,但对排查问题来说少了很多变量。等原型稳定了,再考虑换按需引入优化体积。

2. 环境准备与项目初始化实操

2.1 用 Vite 创建 Vue3 项目

我一般用 npm 这种方式创建:

npm create vite@latest vue-admin-proto -- --template vue

执行完会有几步交互提示,框架选 Vue,语言选 JavaScript 就行。如果你想用 TypeScript,也可以选 vue-ts,但原型阶段用 JS 会更快一些,省去类型定义的时间。

创建完成后,进目录装依赖:

cd vue-admin-proto npm install

装完先跑一下npm run dev,如果浏览器能打开 Vite 默认页,说明基础环境没问题。这里有个小细节:Vite 默认端口是 5173,如果被占用会自动往后顺延,你看到控制台输出的端口为准。

2.2 接入 Element Plus 并验证组件生效

装 Element Plus:

npm install element-plus

然后在main.js里全量引入:

// src/main.js import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import App from './App.vue' import router from './router' const app = createApp(App) app.use(ElementPlus) app.use(router) app.mount('#app')

注意样式文件一定要引入。很多人组件装好了但样式完全不对,十有八九是忘了引index.css

这时候可以把App.vue里 Vite 默认生成的模板清掉,写一个最简单的<el-button>验证:

<template> <el-button type="primary">测试按钮</el-button> </template>

页面上能显示出 Element Plus 样式的按钮,就说明组件库接入成功。这一步看着简单,但它是整个项目的地基,务必确认清楚。

2.3 设计整体布局壳

管理后台的布局基本是固定套路:左侧导航菜单、顶部栏、中间内容区。我一般直接基于 Element Plus 的el-container系列组件来搭,不用去手写 CSS 布局,省时省力。

新建src/layout/AdminLayout.vue

<template> <el-container class="admin-layout"> <el-aside width="220px" class="layout-aside"> <div class="layout-logo">后台原型</div> <el-menu :default-active="activeMenu" router background-color="#f5f7fa" text-color="#333" active-text-color="#409eff" > <el-menu-item index="/dashboard"> <span>仪表盘</span> </el-menu-item> <el-menu-item index="/user-list"> <span>用户管理</span> </el-menu-item> <el-menu-item index="/article-edit"> <span>文章发布</span> </el-menu-item> </el-menu> </el-aside> <el-container> <el-header class="layout-header"> <span>高校新闻网站管理后台</span> </el-header> <el-main class="layout-main"> <router-view /> </el-main> </el-container> </el-container> </template>

这个布局壳有几个关键点。el-menu上开了router属性,这样菜单项的index就会当成路由路径去跳转,省去手动绑定@select事件写跳转逻辑。:default-active="activeMenu"是用来让当前路由对应的菜单保持高亮状态,这里需要在script里通过useRoute()获取当前路径。

2.4 配置基础路由表

有了布局壳,下一步就是把路由表配好。新建src/router/index.js

import { createRouter, createWebHistory } from 'vue-router' import AdminLayout from '../layout/AdminLayout.vue' import Dashboard from '../views/Dashboard.vue' import UserList from '../views/UserList.vue' import ArticleEdit from '../views/ArticleEdit.vue' const routes = [ { path: '/login', name: 'Login', component: () => import('../views/Login.vue') }, { path: '/', component: AdminLayout, redirect: '/dashboard', children: [ { path: 'dashboard', name: 'Dashboard', component: Dashboard }, { path: 'user-list', name: 'UserList', component: UserList }, { path: 'article-edit', name: 'ArticleEdit', component: ArticleEdit } ] } ] const router = createRouter({ history: createWebHistory(), routes }) export default router

这里我用了路由懒加载(() => import())来处理视图组件。原型阶段页面不多,懒加载的优势不明显,但养成这个习惯是好的,等后续页面多了,首屏加载时间能省不少。

还要注意子路由的写法。children里的path不要加/前缀,写成dashboard而不是/dashboard,这样路径会拼接在父路由的路径后面,效果是/dashboard。如果你在子路由里加了/,就会变成根路径,这在嵌套路由里是个特别常见的坑。

按这个配置访问根路径/时,会先挂在AdminLayout,然后通过 children 渲染详情页到<router-view>。登录页不挂布局壳,它是独立页面。

3. 核心页面实现与关键细节

3.1 登录页:从静态界面到动态校验

登录页是管理后台的门面,原型阶段不用做太复杂,但交互逻辑要完整:表单校验、登录按钮的 loading 状态、登录成功跳转首页。

我建议在登录页放一个带动态渐变背景的容器,这样视觉效果比纯白背景好很多。实现方式很简单,用 CSS 动画给背景挂一个渐变色移动效果就行,不需要依赖任何额外的库。

登录表单用el-formel-form-item配合rules做校验。一个基础的登录表单长这样:

<template> <div class="login-page"> <el-card class="login-card"> <h2>后台管理系统登录</h2> <el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" label-position="top" > <el-form-item label="用户名" prop="username"> <el-input v-model="loginForm.username" placeholder="请输入用户名" /> </el-form-item> <el-form-item label="密码" prop="password"> <el-input v-model="loginForm.password" type="password" placeholder="请输入密码" show-password /> </el-form-item> <el-form-item> <el-button type="primary" :loading="loading" class="login-btn" @click="handleLogin" > 登录 </el-button> </el-form-item> </el-form> </el-card> </div> </template>

这里的show-password属性是 Element Plus 自带的密码显隐切换,比手动写一个切换图标方便多了,原型阶段能省则省。

校验规则里,我一般就写必填校验:

const loginRules = { username: [{ required: true, message: '请输入用户名', trigger: 'blur' }], password: [{ required: true, message: '请输入密码', trigger: 'blur' }] }

触发方式trigger: 'blur'是指失焦时校验,对登录这种简单表单够用了。如果你想更严格一点,可以在trigger: 'change'时也校验,但原型阶段没必要。

登录函数需要处理一个细节:

const handleLogin = async () => { if (!loginFormRef.value) return try { await loginFormRef.value.validate() loading.value = true // 模拟登录请求 await new Promise((resolve) => setTimeout(resolve, 800)) localStorage.setItem('token', 'mock-token') router.push('/dashboard') } catch (err) { console.log('登录校验失败:', err) } finally { loading.value = false } }

这里一定要先await validate()再设置 loading。如果你先把 loading 设为 true,再校验,校验失败时按钮会一直处于 loading 状态,还要额外在 catch 里重置,逻辑就绕了。把 validate 放前面,校验失败直接走 catch,按钮完全不会被触发 loading。

3.2 仪表盘:用卡片区展示关键数据

仪表盘(Dashboard)是登录后的落地页,原型阶段放几个统计卡片和占位图表就OK了。

统计卡片可以用el-card+el-row/el-col栅格布局来实现:

<template> <div> <el-row :gutter="20"> <el-col :span="6"> <el-card shadow="hover"> <div class="stat-card"> <p>用户总数</p> <h3>1,286</h3> </div> </el-card> </el-col> <el-col :span="6"> <el-card shadow="hover"> <div class="stat-card"> <p>文章数量</p> <h3>356</h3> </div> </el-card> </el-col> <el-col :span="6"> <el-card shadow="hover"> <div class="stat-card"> <p>今日访问</p> <h3>2,842</h3> </div> </el-card> </el-col> <el-col :span="6"> <el-card shadow="hover"> <div class="stat-card"> <p>待办事项</p> <h3>12</h3> </div> </el-card> </el-col> </el-row> </div> </template>

点开页面能看到这四个卡片在宽屏下一行排开(每列 24 等分中的 6 份,正好 4 个),缩窄浏览器窗口时它们会自动换行,这个栅格特性在后台原型里很实用。

图表部分原型阶段有两个选择:装 ECharts,或者先不装只用占位符。我的建议是:如果图表不是这个原型的演示重点,就先别装 ECharts,放一张灰色占位块就好,把时间留给你真正要验证的功能上。等需要演示数据可视化效果时,再单独引入 ECharts,或者用更轻量的轻量图表库。原型阶段最忌讳什么炫装什么,结果每个都只用了 10% 的功能,维护成本倒是拉满了。

3.3 用户列表:表格数据的查询、分页与操作

用户列表是所有管理后台里出现频率最高的页面,做原型时它的交互完整度直接决定演示效果。一个合格的列表页应该有:搜索条件区、操作按钮区、数据表格、分页器。

我用 Element Plus 的el-tableel-pagination来实现:

<template> <div> <el-card> <el-form inline> <el-form-item label="用户名"> <el-input v-model="queryParams.username" placeholder="搜索用户名" clearable /> </el-form-item> <el-form-item label="状态"> <el-select v-model="queryParams.status" placeholder="全部" clearable style="width: 140px"> <el-option label="启用" value="active" /> <el-option label="禁用" value="disabled" /> </el-select> </el-form-item> <el-form-item> <el-button type="primary" @click="handleSearch">查询</el-button> <el-button @click="handleReset">重置</el-button> </el-form-item> </el-form> </el-card> <el-card style="margin-top: 16px"> <div style="margin-bottom: 16px"> <el-button type="primary" @click="handleAdd">新增用户</el-button> </div> <el-table :data="tableData" border stripe> <el-table-column prop="id" label="ID" width="80" /> <el-table-column prop="username" label="用户名" width="140" /> <el-table-column prop="email" label="邮箱" /> <el-table-column prop="status" label="状态" width="100"> <template #default="{ row }"> <el-tag :type="row.status === 'active' ? 'success' : 'info'"> {{ row.status === 'active' ? '启用' : '禁用' }} </el-tag> </template> </el-table-column> <el-table-column label="操作" width="220"> <template #default="{ row }"> <el-button link type="primary" @click="handleEdit(row)">编辑</el-button> <el-button link type="danger" @click="handleDelete(row)">删除</el-button> </template> </el-table-column> </el-table> <el-pagination style="margin-top: 16px; justify-content: flex-end" v-model:current-page="queryParams.page" v-model:page-size="queryParams.pageSize" :total="total" :page-sizes="[10, 20, 50]" layout="total, sizes, prev, pager, next" @size-change="fetchData" @current-change="fetchData" /> </el-card> </div> </template>

这个页面有几个细节值得讲。

inline属性的el-form会自动把表单项排成一行,这是搜索区最常用的形态。搜索区里的clearable属性很重要,它会在输入框右侧出现一个清空图标,演示时手动清空条件很方便,不用删了再敲。

表格的v-model:current-pagev-model:page-size是 Element Plus 分页组件提供的双向绑定语法,比手动监听current-change事件再去改数据简单。layout属性控制分页器显示哪些部分,total, sizes, prev, pager, next是标配,带上sizes后用户可以自己切换每页条数。

表格里的状态列用el-tag渲染成彩色标签比纯文本直观得多,在后台原型里是个很出效果的细节。注意我用了作用域插槽#default="{ row }"来获取当前行数据,这是 Element Plus 表格常见的用法。

数据获取在原型阶段用 mock 函数模拟就行:

const mockFetchList = () => { return new Promise((resolve) => { setTimeout(() => { resolve({ total: 58, rows: [ { id: 1, username: 'zhangsan', email: 'zhangsan@example.com', status: 'active' }, { id: 2, username: 'lisi', email: 'lisi@example.com', status: 'disabled' } // 更多数据... ] }) }, 500) }) }

注意这里用pagepageSize拼接出当前要返回的数据。原型阶段数据乱一点没关系,只要列表能翻页、条数对得上,演示效果就够真实。

3.4 表单页:结合 Tab 切换的复杂表单布局

表单页也是后台的高频场景。新建一个"文章发布"页面,演示一个稍微复杂一点的表单:标题、所属栏目、内容摘要、封面上传、发布时间、正文编辑,这些字段要能正常输入,还要有校验。

这里我推荐用el-tabs把表单拆成"基本信息"和"高级设置"两个 Tab。这样既能展示 Tab 组件,又让页面看起来结构清晰,不会一上来就堆一个超长表单吓到看演示的人。

<template> <el-card> <el-tabs v-model="activeTab"> <el-tab-pane label="基本信息" name="basic"> <el-form ref="articleFormRef" :model="articleForm" :rules="formRules" label-width="100px"> <el-form-item label="文章标题" prop="title"> <el-input v-model="articleForm.title" placeholder="请输入文章标题" /> </el-form-item> <el-form-item label="所属栏目" prop="category"> <el-select v-model="articleForm.category" placeholder="请选择栏目" style="width: 240px"> <el-option label="校园新闻" value="campus" /> <el-option label="学术动态" value="academic" /> <el-option label="通知公告" value="notice" /> </el-select> </el-form-item> <el-form-item label="发布时间" prop="publishTime"> <el-date-picker v-model="articleForm.publishTime" type="datetime" placeholder="选择发布时间" /> </el-form-item> <el-form-item label="文章摘要" prop="summary"> <el-input v-model="articleForm.summary" type="textarea" :rows="3" placeholder="请输入文章摘要" /> </el-form-item> <el-form-item> <el-button type="primary" @click="handleSubmit">发布</el-button> <el-button @click="handleReset">重置</el-button> </el-form-item> </el-form> </el-tab-pane> <el-tab-pane label="高级设置" name="advanced"> <el-form label-width="100px"> <el-form-item label="封面图片"> <el-upload action="#" :auto-upload="false" :limit="1" list-type="picture-card" > <span>上传封面</span> </el-upload> </el-form-item> <el-form-item label="置顶显示"> <el-switch v-model="articleForm.isTop" /> </el-form-item> <el-form-item label="允许评论"> <el-switch v-model="articleForm.commentEnabled" /> </el-form-item> </el-form> </el-tab-pane> </el-tabs> </el-card> </template>

用 Tab 拆表单有一个要注意的点:如果高级设置里的字段也参与提交校验,那么校验时机得处理好。最简单的做法是发布按钮放在"基本信息"里,点击发布时只校验基本信息 Tab 内的字段,高级设置里的开关都给了默认值,不影响提交。这样交互逻辑最简单,原型演示也不容易翻车。

el-upload这里我设置了:auto-upload="false",意思是不自动上传,只在本地把文件列表管理好。原型阶段没有后端接收上传,你如果让它自动上传,会直接请求当前地址,控制台一片报错,演示体验很糟糕。把 auto-upload 关掉就没人会发现后端还没接。

3.5 菜单与路由联动:让当前菜单高亮

前面布局壳里我留了一个:default-active="activeMenu",现在需要把它和useRoute()关联起来:

<script setup> import { computed } from 'vue' import { useRoute } from 'vue-router' const route = useRoute() const activeMenu = computed(() => { return route.path }) </script>

就这么简单。route.path是当前访问的路径,el-menudefault-active拿它和el-menu-itemindex做匹配,高亮就自动切换了。

有一个容易忽略的细节:当路由 path 和菜单 index 不完全相等时,比如某个菜单下有子页面/user-list/detail/1,用route.path就没法匹配到/user-list。原型阶段一般不会有这么深的嵌套,但如果遇到,可以改用route.matched数组来找到上级路由的 path。这个先了解即可,真遇到再处理。

4. 接口模拟、交互增强与常见问题实录

4.1 前端页面怎么"连接"后端:先学会用 Mock

管理后台原型在没接后端时,也要保证演示流畅,所以前端的数据请求不能真的发出去,需要 mock 掉。我一般不用 Mock.js 这类库,直接在src/api/mock.js里写返回 Promise 的函数就行。好处是简单直接,没有额外依赖;坏处是后面接真实接口时,要把这些 Promise 函数改成 axios 请求。

如果确实想提前体验真实的请求生命周期,可以装 axios,然后用一个baseURL指向一个不存在的本地服务,同时配好proxy。但原型阶段我更推荐纯 Promise mock,因为不存在网络错误打断演示的问题,演示时最怕页面弹一堆红色报错。

4.2 Element Plus 中 Tab 切换和数据加载的坑

使用el-tabs时有一个比较隐蔽的问题:如果 Tab 面板里放的是请求数据的组件,而请求是在组件初始化时发起的,那每次切换 Tab 都会重新触发请求。原型阶段数据是 mock 的看不出问题,但如果后面接真实接口,会带来不必要的重复请求。

解决方案有两种。一种是在 Tab 面板外层包一层v-show而不是v-if,这样面板组件不会被销毁重建。另一种是在父组件里缓存数据,首次加载后存起来,后续切换 Tab 直接读缓存,不再发请求。原型阶段不用急着优化,但要知道这个问题存在。

4.3 Vue3 中常用踩坑:props 传递、路由切换不渲染、diff 差异

写 Vue3 时,有几个问题是新手高频踩坑点,我在这里集中说一下。

props 给子组件传值,子组件里想直接改,这是最常见的误区。比如父组件传了一个userInfo对象给子组件,子组件里直接userInfo.name = 'xxx',虽然 Vue3 里这是能改的(因为对象是引用类型),但会触发告警,而且是反模式。正确做法是把 props 当只读数据源,如果要修改,可以复制一份到组件内部的ref,或者通过emit事件通知父组件改。具体操作是在子组件里:

const props = defineProps({ userInfo: { type: Object, default: () => ({}) } }) const localInfo = ref({ ...props.userInfo })

这样localInfo是独立的副本,随便改不会影响父组件的状态。

路由跳转后组件内容不渲染,这个问题的原因十有八九是路由写挂或者router-view位置不对。尤其是嵌套路由时,如果你在父组件里忘了写<router-view>,子路由页面永远不会显示。排查思路是:先在控制台输入window.location.pathname看看路径对不对,再检查父路由的组件模板里有没有<router-view>,最后确认路由的children路径没加/。按照这个顺序查,基本都能解决。

Vue3 的diff算法相比 Vue2 有变化,但这更多是运行时层面的机制,原型阶段写代码时你不需要刻意关心。只需要知道一个点:用v-for渲染列表时,:key一定要写,而且不能拿数组下标当 key。因为 Vue 的复用机制依赖 key 来识别节点,如果数据顺序变化了,用下标当 key 会导致本不该复用的节点被复用,状态会错乱。开发中如果遇到列表渲染后数据"跟错位"了,先检查 key 是不是没写或者是下标。

4.4 样式覆盖 Element Plus 的两种有效姿势

后台原型经常要调整 Element Plus 组件的默认样式,比如修改标签页样式、表格表头背景色、菜单栏选中颜色等。这里有两种姿势,我分别说明。

第一种是全局覆盖:在src/styles/下新建一个 CSS 文件,然后直接写:root变量或者类名覆盖,最后在main.js里引入。比如想修改主色调:

:root { --el-color-primary: #635bff; }

Element Plus 的样式大量使用 CSS 变量,光改这一个变量,就能把主按钮、选中态、焦点框等一堆相关颜色统一改掉。这个思路比去逐个覆盖类名省事得多。

第二种是局部覆盖:用deep选择器调整 scoped 样式。比如想给表格表头加个底色:

<style scoped> .lookup-table :deep(.el-table__header th) { background-color: #f5f7fa; } </style>

注意这行代码的用法::deep()是 Vue3 中穿透 scoped 样式的写法,如果你用的是 Vue2,对应的写法是/deep/>>>,差别要注意。写样式覆盖时,最好给最外层容器加一个唯一的类名(比如lookup-table),避免样式影响其他页面的同名组件。

还有个细节:Element Plus 的el-table表头默认背景是白色偏透明,加上边框后视觉上还行。如果你要改成灰色表头,用我上面这种方式就能一步到位,不用去搜什么"Element Plus 表格改色"半天找不对地方。

4.5 常见问题速查表

我把原型开发中出镜率最高的几个问题整理成了一张表,遇到直接对照排查:

现象可能原因排查思路
组件没有样式没引入element-plus/dist/index.css检查 main.js 样式引入
路由跳转后白屏父组件缺<router-view>检查 layout 组件模板
路由子页面 404children 的 path 加了/前缀改成相对路径
按钮点击无反应事件函数没定义或没绑定看控制台是否报错
表格错位el-table-column数量不匹配检查列配置
表单校验不生效prop值和表单项 name 不一致对齐 prop 与即可
页面启动报错Node 版本过旧升级 Node 18+
热更新偶发失效Vite 缓存异常重启 dev server,必要时删node_modules重装

这里第八条我多提一句:原型阶段项目很小,如果遇到奇怪的报错,最直接的办法就是备份源码后删掉node_modules重新npm install。很多看起来很难解决的环境问题,重装依赖后就消失了。原型阶段不值得花太多时间深究环境问题,重新来最快。

4.6 从原型到真实项目的过渡建议

原型跑通后,如果要往真实项目方向走,有几个改动点需要提前留意。

第一是接入真实接口。把src/api/mock.js里的 Promise 函数改成 axios 请求,加上拦截器统一处理 token 和错误提示。推荐用 Vite 的server.proxy配置开发环境代理,这样前端请求就始终走/api前缀,不会出现跨域问题。

// vite.config.js export default defineConfig({ plugins: [vue()], server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })

第二是加路由守卫。原型阶段直接访问/dashboard也能打开,真实项目里这是不允许的。在router/index.js里加一个beforeEach

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.path !== '/login' && !token) { next('/login') } else { next() } })

这样没有 token 的用户访问/dashboard会被重定向到登录页。配合登录时写入 token、退出时清掉 token,一个最简权限闭环就形成了。

第三是判断要不要上 TypeScript。原型阶段用 JS 是更快,但如果项目确认要长期迭代,建议尽早切换到 TS。管理后台的接口数据、表单数据、路由配置都有清晰的结构,TS 能帮你减少大量因字段拼写错误导致的低级 bug。不过切换时机要选好,最好在页面数量还不算多的时候做,不然重构成本会指数上升。

5. 一些实操心得与后续扩展方向

我实际搭过好几套后台原型,说几个真实的体验。

第一,原型阶段的"丑"是可以接受的。很多人一开始就想把界面弄得特别漂亮,结果一半时间花在调 CSS 上,核心流程反而不完整。我现在的策略是:第一版只保证功能链路通,菜单能点、列表能翻、表单能填、状态有反馈,样式用 Element Plus 默认主题,最多改一下主色。等流程走通了,回头再调样式,效率反而更高。

第二,命名规范在原型阶段就要注意。views下的文件名、路由的name、组件的名称,尽量用同一套命名风格。我习惯用短横线命名法写文件名(user-list.vue),用大写驼峰命名路由的nameUserList)。这样开发久了不会乱,Element Plus 的name和路由name对得上,排查问题也更快。

第三,组件拆分不要过度。原型阶段一个页面 200 行以内都能接受,没必要把每个表格都拆成一个components/TableList.vue。拆组件的收益在页面复用时才体现得出来。你硬拆成很多小组件,各种 props、emit 反而增加了理解成本。

关于后续扩展,我建议可以从三个方向继续加料。

一是把动态菜单做出来。目前菜单是写死在布局里的,真实项目里菜单一般来自后端返回的路由配置。可以做一个简单版本:登录后从接口拉菜单数据,用router.addRoute动态注册路由。这个功能一加上,原型的完整度会提升一个档次。

二是有意义地接入状态管理。目前项目没有引入 Pinia,因为原型阶段组件间共享状态少,reactiveref就够用。等登录用户信息、菜单权限、多个页面共享的数据多起来时,再上 Pinia,管理起来会清晰很多。

三是考虑一键生成代码的尝试。如果原型最后要交给开发照着写,可以试试用vue-element-plus-admin之类的开源模板做对照参考。不过我的建议是:先自己搭一遍原型,再用模板查漏补缺,这样你既能看懂模板里那些封装在做什么,又能结合自己的原型快速找到需要的代码。纯依赖模板而不理解原理,后面出了问题还是两眼一抹黑。

回到开头说的——"能跑起来"比"看着高级"重要太多。跟着这篇文章搭出来的原型,它的意义不在于它能拿出去给客户做宣传页面,而在于你亲手建立了从npm create vite到页面渲染的完整链路。这条路走通之后,后面不管是换 UI 库、加权限、接接口,都是在这个地基上加砖,心里有底。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询