毕业设计实战:基于 PHP + Vue 的前后端分离架构设计与避坑指南
面向对象:计算机专业、有 HTML/CSS/JS 与一学期 PHP 基础、正准备肝毕设的你
目标:40 天内交付一套“能跑、能讲、能答辩”的前后端分离项目,拒绝“本地全绿,上线全红”。
一、先吐槽:那些年我们一起踩过的毕设坑
前后端一锅炖
把 Vue 打包后的dist直接塞进 Laravel 的public目录,本地php artisan serve一切正常,放到虚拟主机后 404 比接口还多。接口口头约定
前端:“/api/login 返回啥?” 后端:“你看了就知道。” 结果字段一改,Vue 连夜重写。跨域玄学
Chrome 控制台CORS policy blocked红到发慌,百度一圈header('Access-Control-Allow-Origin: *')到处乱丢,依旧被预检请求教做人。部署当天爆炸
.env里APP_DEBUG=true忘了关,线上直接打印数据库密码;或者npm run build后静态资源 404,导师一句“演示不了就二辩”让人瞬间清醒。
二、技术选型:为什么 Laravel + Vue?
| 维度 | Laravel | ThinkPHP | Vue | React |
|---|---|---|---|---|
| 学习曲线 | 文档全、社区大、脚手架开箱即用 | 轻量但生态偏中文 | 渐进式,模板语法贴近 HTML | JSX 语法对新手不友好 |
| 内置功能 | 路由、ORM、队列、JWT 扩展一键装 | 需要额外装库 | 官方 CLI 工具 + vite 秒级热更新 | 同左,但配置项多 |
| 毕业设计答辩 | 老师听得懂“Laravel” | 容易被问“为什么不用 Laravel” | 与 Element-Plus 搭配 UI 好看 | 状态管理概念多,讲不清 |
| 上线托管 | PHP 主机 9.9 元/月遍地 | 同左 | 静态页可丢 CDN | 同左 |
结论:
“PHP 选 Laravel,前端选 Vue” = 老师放心、自己省心、钱包开心。
三、架构鸟瞰图
- 前端:Vue3 + Vite + Axios + Element-Plus,独立跑在
localhost:5173 - 后端:Laravel10 + MySQL + JWT,独立跑在
localhost:8000 - 交互:纯 RESTful JSON,CORS 全开,静态资源走 CDN,文件上传走 OSS
四、后端关键实现(Laravel 10.x)
1. 路由与控制器——“先写注释再写代码”
// routes/api.php Route::prefix('v1')->group(function () { Route::post('login', [AuthController::class, 'login']); // 登录 Route::middleware('auth:api')->group(function () { Route::apiResource('projects', ProjectController::class); // 增删改查 }); });// app/Http/Controllers/AuthController.php namespace App\Http\Controllers; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Tymon\JWTAuth\Facades\JWTAuth; class AuthController extends Controller { /** * 登录:学号+密码 */ public function login(Request $request) { $credentials = $request->validate([ 'student_id' => 'required|string', 'password' => 'required|string|min:6' ]); $user = User::where('student_id', $credentials['student_id'])->first(); if (! $user || ! Hash::check($credentials['password'], $user->password)) { return response()->json(['error' => 'Unauthorized'], 401); } $token = JWTAuth::fromUser($user); return response()->json([ 'access_token' => $token, 'token_type' => 'bearer', 'expires_in' => auth()->factory()->getTTL() * 60 ]); } }2. JWT 安装与配置
composer require tymon/jwt-auth php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider" php artisan jwt:secret # 生成 .env 中 JWT_SECRET修改app/Models/User.php,实现JWTSubject接口,具体代码略(官方文档复制即可)。
3. 统一响应格式——让前端不再“拆盲盒”
// app/Traits/ApiResponse.php trait ApiResponse { protected function ok($data = [], string $msg = 'success') { return response()->json(['code' => 0, 'msg' => $msg, 'data' => $data]); } protected function fail(string $msg = 'error', int $code = 500) { return response()->json(['code' => $code, 'msg' => $msg, 'data' => null]); } }在控制器里use ApiResponse;即可return $this->ok($projects);
五、前端关键实现(Vue 3 + Vite)
1. Axios 封装——“一处配置,全局爽用”
// src/utils/request.js import axios from 'axios' import { ElMessage } from 'element-plus' import router from '@/router' const service = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, // .env 文件里配置 timeout: 6000 }) // 请求拦截:自动携带 JWT service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) config.headers.Authorization = `Bearer ${token}` return config }) // 响应拦截:统一弹窗 + 自动跳登录 service.interceptors.response.use( response => { const { code, msg, data } = response.data if (code !== 0) { ElMessage.error(msg || 'Server Error') return Promise.reject(new Error(msg)) } return data }, error => { if (error.response?.status === 401) { router.push('/login') } return Promise.reject(error) } ) export default service2. 登录组件——“双向绑定 + 表单校验”
<!-- src/views/Login.vue --> <template> <el-form :model="form" :rules="rules" ref="loginForm"> <el-form-item prop="student_id"> <el-input v-model="form.student_id" placeholder="学号"></el-input> </el-form-item> <el-form-item prop="password"> <el-input v-model="form.password" type="password" placeholder="密码"></el-input> </el-form-item> <el-button type="primary" @click="handleLogin">登录</el-button> </el-form> </template> <script setup> import { reactive, ref } from 'vue' import { useRouter } from 'vue-router' import request from '@/utils/request' const router = useRouter() const loginForm = ref() const form = reactive({ student_id: '', password: '' }) const rules = { student_id: [{ required: true, message: '请输入学号', trigger: 'blur' }], password: [{ required: true, message: '请输入密码', trigger: 'blur' }] } const handleLogin = async () => { await loginForm.value.validate() const res = await request.post('/v1/login', form) localStorage.setItem('token', res.access_token) router.push('/') } </script>3. 列表组件——“分页+删除二次确认”
<template> <el-table :data="projects"> <el-table-column prop="name" label="项目名称"></el-table-column> <el-table-column label="操作"> <template #default="{row}"> <el-button link type="danger" @click="remove(row.id)">删除</el-button> </template> </el-table-column> </el-table> <el-pagination background layout="prev, pager, next" :total="total" @current-change="loadProjects"> </el-pagination> </template> <script setup> import { onMounted, ref } from 'vue' import request from '@/utils/request' const projects = ref([]) const total = ref(0) const loadProjects = async (page = 1) => { const data = await request.get('/v1/projects', { params: { page } }) projects.value = data.data total.value = data.total } const remove = async id => { try { await ElMessageBox.confirm('确认删除?') await request.delete(`/v1/projects/${id}`) ElMessage.success('删除成功') loadProjects() } catch {} } onMounted(() => loadProjects()) </script>六、安全与性能:把“能跑”升级成“能扛”
XSS 防护
Laravel 自带{{ }}转义输出;Vue 默认模板也是转义。切忌用{!! !!}或v-html渲染用户富文本,除非上 DOMPurify。CSRF 防护
前后端分离后,JWT 代替 Session,CSRF 风险降低。但上传接口仍需白名单校验,别图省事直接csrf: off。SQL 注入
用 Eloquent ORM 而不是裸写 SQL;如必须裸写,用参数绑定。本地开发提速
- Laravel:开
php artisan serve --host=0.0.0.0,装 Laravel Octane 更酸爽 - Vue:Vite 配置
server.hmr = true,装vite-plugin-mkcert秒开 HTTPS,避免 Mixed-Content
- Laravel:开
七、生产环境避坑指南
.env管理
线上绝不出现APP_DEBUG=true;把.env加入.gitignore,CI 用envsubst注入敏感变量。静态资源
npm run build后把dist目录推到 CDN,Laravel 只当纯 API,避免public/mix-manifest.json404。API 版本控制
路由加v1/前缀,控制器命名空间留App\Http\Controllers\Api\V1,后续升级v2时,老版本项目不动。队列与任务
导出 Excel、发邮件走队列,本地用sync,线上切redis + supervisor,防止请求超时。日志与监控
Laravel 每日志按天切割,装laravel-log-viewer随时看;前端 Sentry 上报,别等导师打电话才知道 500。
八、一键脚手架 & 彩蛋思考
我把上面所有代码打包成 GitHub 模板,搜索“laravel-vue-jwt-starter”即可 clone,直接composer install && npm install跑起。
下一步,你可以:
- 给登录接口加上“幂等性校验”(如重放攻击场景),试试用 JWT + Jti 黑名单
- 把文件上传到 OSS,接入分片上传,写进简历秒变“分布式存储”
- 用 Docker Compose 把 PHP-FPM、MySQL、Redis、Nginx 一次编排,答辩现场
docker-compose up直接秀翻
毕业设计不是“代码坟墓”,而是“大型技术脱口秀”。
把 Laravel + Vue 这条链路跑通,你不仅能在答辩时自信地指着屏幕说“这是 RESTful,这是 JWT,这是 CORS”,更能把“踩坑—填坑—优化”的过程写进简历,让面试官看到你会“解决问题”,而不只是“写过增删改查”。
现在,轮到你动手:
- fork 脚手架
- 换个业务场景(二手书、实验室预约、校园跑腿……)
- 跑通本地,push 到云主机
- 打开 README,把“接口幂等性”写成第一个 TODO
祝你 40 天后顺利通关,答辩现场只说“演示完毕,请老师提问”。