资讯动态

用 Vue 3 + TypeScript + Laravel 串起一次完整请求

发布时间:2026/10/14 16:39:56 来源:尧图企业网站定制
从点击提交到数据库用 Vue 3 TypeScript Laravel 串起一次完整请求 本文适合正在学习前后端分离的开发者。我们用一个“创建任务”的小功能走完从页面表单、TypeScript 类型、HTTP 请求、Laravel 校验到数据库写入的完整链路。一、为什么第一篇全栈文章从一个小功能开始很多人学习全栈开发时会分别学习 Vue、TypeScript、PHP、Laravel 和 MySQL但真正开始做功能时仍然容易卡在“前端和后端到底怎么配合”这个问题上。一个完整功能至少包含下面几件事1. 前端收集用户输入。2. 前端按照约定发送请求。3. 后端验证输入是否合法。4. 后端执行业务逻辑并写入数据库。5. 后端返回统一格式的数据。6. 前端根据成功或失败状态更新页面。本文不追求一次性搭建复杂后台而是先把这条链路走通。只要理解了这条链路登录、评论、订单和后台管理等功能都可以继续拆解。二、示例功能和接口约定我们实现一个简单的任务创建接口textPOST /api/tasks请求体json{title: 整理 TypeScript 类型,description: 为任务接口补充请求和响应类型,priority: high}成功响应json{message: 任务创建成功,data: {id: 1,title: 整理 TypeScript 类型,description: 为任务接口补充请求和响应类型,priority: high,created_at: 2026-10-13T10:00:00.000000Z}}这里最重要的不是字段数量而是前后端在请求方法、URL、字段名和响应结构上达成一致。接口契约越清晰联调时的猜测就越少。三、Laravel 后端先建立数据结构1、创建数据表创建迁移文件bashphp artisan make:model Task -m在迁移文件中定义任务表phpuse Illuminate\Database\Migrations\Migration;use Illuminate\Database\Schema\Blueprint;use Illuminate\Support\Facades\Schema;return new class extends Migration{public function up(): void{Schema::create(tasks, function (Blueprint $table) {$table-id();$table-string(title, 120);$table-text(description)-nullable();$table-enum(priority, [low, normal, high])-default(normal);$table-timestamps();});}public function down(): void{Schema::dropIfExists(tasks);}};执行迁移bashphp artisan migrate2、配置模型的可填充字段phpnamespace App\Models;use Illuminate\Database\Eloquent\Model;class Task extends Model{protected $fillable [title,description,priority,];}$fillable 是一道很有价值的保护。它明确规定哪些字段允许通过批量赋值写入避免把客户端传来的任意字段直接交给模型。四、Laravel 后端路由、校验和保存1、定义 API 路由在 routes/api.php 中添加phpuse App\Http\Controllers\TaskController;use Illuminate\Support\Facades\Route;Route::post(/tasks, [TaskController::class, store]);创建控制器bashphp artisan make:controller TaskController2、编写创建接口phpnamespace App\Http\Controllers;use App\Models\Task;use Illuminate\Http\JsonResponse;use Illuminate\Http\Request;class TaskController extends Controller{public function store(Request $request): JsonResponse{$validated $request-validate([title [required, string, max:120],description [nullable, string, max:2000],priority [required, in:low,normal,high],]);$task Task::create($validated);return response()-json([message 任务创建成功,data $task,], 201);}}这段代码包含三个关键决策- 使用 $request-validate() 把非法输入挡在业务逻辑之前。- 使用模型的 $fillable 控制可写字段。- 创建成功返回 HTTP 201让调用方能够区分“创建成功”和普通查询成功。Laravel 在校验失败时会返回 422 Unprocessable Entity。前端只要针对这个状态读取错误字段就能把错误提示展示在对应表单项旁边。五、Vue 3 TypeScript先定义类型再发送请求1、定义请求和响应类型tsexport type TaskPriority low | normal | highexport interface CreateTaskPayload {title: stringdescription?: stringpriority: TaskPriority}export interface Task {id: numbertitle: stringdescription: string | nullpriority: TaskPrioritycreated_at: string}export interface ApiResponseT {message: stringdata: T}类型的价值不只是让编辑器有提示。它把接口契约变成了代码的一部分当后端字段变更时前端可以更早发现问题而不是等到页面运行后才看到 undefined。2、封装 API 请求下面以 Axios 为例tsimport axios from axiosimport type { ApiResponse, CreateTaskPayload, Task } from ./typesconst http axios.create({baseURL: import.meta.env.VITE_API_BASE_URL,timeout: 10000,headers: {Accept: application/json,Content-Type: application/json,},})export async function createTask(payload: CreateTaskPayload,): PromiseApiResponseTask {const response await http.postApiResponseTask(/tasks, payload)return response.data}环境变量示例envVITE_API_BASE_URLhttp://localhost:8000/api把基础地址集中管理后开发环境、测试环境和生产环境可以使用不同配置业务代码不需要反复修改 URL。六、Vue 页面提交、加载和错误状态都要有vuescript setup langtsimport { reactive, ref } from vueimport { createTask } from /api/tasksimport type { CreateTaskPayload } from /api/typesconst form reactiveCreateTaskPayload({title: ,description: ,priority: normal,})const submitting ref(false)const message ref()const errorMessage ref()async function handleSubmit() {message.value errorMessage.value if (!form.title.trim()) {errorMessage.value 请输入任务标题return}submitting.value truetry {const result await createTask({...form,title: form.title.trim(),})message.value result.messageform.title form.description form.priority normal} catch (error: any) {if (error.response?.status 422) {errorMessage.value 提交内容不符合要求请检查表单} else {errorMessage.value 网络异常请稍后重试}} finally {submitting.value false}}/scripttemplateform submit.preventhandleSubmitlabel任务标题input v-modelform.title maxlength120 //labellabel任务描述textarea v-modelform.description maxlength2000 //labellabel优先级select v-modelform.priorityoption valuelow低/optionoption valuenormal普通/optionoption valuehigh高/option/select/labelbutton typesubmit :disabledsubmitting{{ submitting ? 提交中... : 创建任务 }}/buttonp v-ifmessage{{ message }}/pp v-iferrorMessage{{ errorMessage }}/p/form/template前端校验是为了改善用户体验后端校验是为了保证系统安全。两者不能互相替代即使页面已经限制了长度服务端仍然必须再次校验。七、一次请求到底发生了什么点击“创建任务”后数据大致经过下面的路径textVue 表单- handleSubmit- createTask()- POST /api/tasks- Laravel 路由- TaskControllerstore- Request 验证- Task 模型- MySQL tasks 表- JSON 响应- Vue 更新页面排查问题时也可以沿着这条路径定位- 页面没有请求检查表单事件和按钮是否被禁用。- 请求地址错误检查 VITE_API_BASE_URL 和路由前缀。- 返回 422查看后端校验规则和请求字段名。- 返回 500查看 Laravel 日志和数据库迁移状态。- 浏览器跨域检查 Laravel CORS 配置和前端请求来源。八、这类项目最容易忽略的三个细节1、不要让前端猜接口字段title、task_title 和 name 对人来说都像标题对程序来说却是三个不同字段。接口文档、TypeScript 类型和后端校验应当使用同一套命名。2、不要只处理成功状态真实网络环境中超时、断网、校验失败和权限失效都很常见。页面至少应该有提交中、成功、参数错误和网络异常四种状态。3、不要把所有逻辑都塞进控制器本文的功能很小把保存逻辑放在控制器中还可以接受。当业务继续增长时可以把复杂逻辑提取到 Action、Service 或领域层让控制器只负责接收请求和返回响应。九、后续可以怎样扩展完成任务创建后可以继续添加- GET /api/tasks分页查询任务。- PATCH /api/tasks/{task}更新任务状态。- DELETE /api/tasks/{task}删除任务。- Laravel Sanctum为接口增加登录认证。- Pinia集中管理用户信息和任务列表。- React TypeScript用同一份接口契约重写前端页面。- PHPUnit 和前端测试验证校验规则与交互状态。这也是我理解全栈开发的方式不是把前端和后端知识简单堆在一起而是围绕一个业务闭环把页面、接口、数据和异常处理连接起来。十、总结一个看似简单的“创建任务”功能已经覆盖了全栈开发中最重要的协作边界- Vue 负责交互和状态展示。- TypeScript 负责描述数据结构。- Laravel 负责路由、校验和业务入口。- Eloquent 负责模型与数据库之间的映射。- API 契约负责让前后端按照同一种语言协作。当你能独立完成这条链路再去学习登录、权限、文件上传和实时通信学习过程会清晰很多。---参考阅读本文仅参考了 CSDN 上公开文章常见的组织方式和问题场景代码与表述均为重新设计1. [基于 Laravel 和 Vue 的前后端分离设计](https://blog.csdn.net/abc8125/article/details/116293678)2. [Laravel9 使用 Vue3 前后端分离实现对接验证码](https://blog.csdn.net/qq_36901092/article/details/129929767)3. [PHP 框架 Laravel Vue3 前后端分离开发模式 实战项目](https://blog.csdn.net/qq_37292133/article/details/138343054)

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑