做全栈开发这些年,前后端技术选型我试过不少组合,从早期的服务端模板渲染,到后来的前后端分离,兜兜转转,Vue+Laravel这套组合是我用得最顺手、也最愿意推荐给团队新人的一套方案。Laravel把后端那些繁琐的事——路由、ORM、认证、队列——安排得明明白白,Vue则用组件化的思路把前端页面拆得清清楚楚,两者搭配干活,开发效率确实能拉开一截。
这篇文章不是框架说明书,而是我从一个完整的全栈项目出发,把Vue3+Laravel11全栈开发的完整流程拆开讲清楚:从环境搭建、API设计、前后端联调,到认证鉴权、部署上线,每一步都配上实操代码和踩坑记录。适合有PHP和JavaScript基础、想系统掌握全栈开发的读者,也适合已经在用这套技术栈、遇到不少问题想找解决方案的朋友。
1. 整体设计与技术选型思路
1.1 为什么是Vue+Laravel,而不是其他组合
先聊一个很多人纠结的问题:全栈开发到底选什么技术栈?我个人的判断标准有三条:开发效率、生态成熟度、以及团队协作的顺畅程度。
Laravel作为PHP社区最主流的框架,最大的优势是“约定优于配置”做得极其到位。路由、数据库迁移、ORM、认证系统都是开箱即用的,你不需要在项目里堆一堆第三方库才能跑起来。而且Laravel的文档和社区资源相当丰富,遇到问题很容易搜到答案。PHP本身的部署成本也低,普通云服务器跑起来毫无压力,这对中小型项目和外包开发来说非常友好。
Vue这边的优势是渐进式架构。你可以只在一个页面里引入Vue做局部交互,也可以配合Vite、Pinia、Vue Router搭一个完整的单页应用。Vue的响应式系统和单文件组件设计得很贴近直觉,模板语法对后端转前端的开发者尤其友好——没有JSX那种满屏逻辑的割裂感,写起来更像是在写HTML。
对比过其他组合之后,我的体感是:Spring Boot+React功能强大,但重;Django+Vue也不错,但Python在Web领域的生态重心不一定在传统服务端渲染上;Node.js后端写起来爽,但项目一大的维护成本反而高。Vue+Laravel恰好站在了“够用、够稳、够快”的平衡点上。
1.2 两种开发模式的选型取舍
确定了技术栈,紧接着要决定前后端的组织方式。这里有两套主流方案:
第一种是完全前后端分离:Laravel只提供API接口,Vue单独起一个项目,通过HTTP请求和JSON数据交互。前端独立部署在Nginx上,后端独立部署,两者通过域名或路径区分。这套模式的优点是职责清晰,前端可以完全交给前端工程师,后端专注于业务逻辑和API设计。缺点是跨域问题、Token管理和部署环节都会多出一些工作。
第二种是Laravel + Inertia.js + Vue的组合:Inertia是一个让前端框架和后端框架共享路由和组件的桥接工具,页面跳转由Laravel控制,渲染交给Vue。这套方案没有真正的API层,页面数据直接由控制器传给Vue组件,跨域问题和Token管理都不存在了。它的开发体验非常顺滑,特别适合一个人单干或者小团队快速交付。
从我的实践经验来看,如果你的项目需要多端支持——比如同时有Web端、小程序、App——那就老老实实选完全分离模式,API一次开发多端复用。如果项目就是纯Web后台管理系统或者内部工具,选Inertia模式,开发速度能再提升一截。
1.3 项目结构规划与团队分工
我习惯用“单仓库双目录”的方式组织项目:
project-root/ ├── backend/ # Laravel 后端(API) │ ├── app/ │ ├── routes/ │ ├── database/ │ └── ... └── frontend/ # Vue 前端(SPA) ├── src/ ├── vite.config.js └── ...这套结构的好处是版本管理方便,一个仓库搞定前后端,团队成员pull下来就能跑,不用分别维护两个仓库再处理版本匹配问题。缺点是需要让构建工具知道如何区分前后端的依赖,比如根目录放一个docker-compose.yml统一管理数据库、后端、前端的容器,或者写成两份说明文档分别交代启动流程。
如果是二到五人的小团队,我建议让后端工程师负责API定义和核心逻辑,前端工程师专注于Vue的组件和状态管理,但每个人都要完整跑通一遍联调和部署。全栈不是说一个人干所有事,而是每个人都知道整个系统的运行逻辑,协作起来才不至于出现“接口字段前后面改了三遍”的尴尬。
2. 开发环境准备与项目初始化
2.1 环境依赖清单与版本说明
开始之前,先确认本地环境版本。我用的是一套经过多轮测试的稳定组合,直接照着装即可:
- PHP 8.2+(必须,Laravel 11要求PHP 8.2)
- Composer 2.x(依赖管理)
- Node.js 20+(Vite 5要求)
- 数据库:MySQL 8.0或PostgreSQL 16,二选一即可
- Git(版本管理,务必配置好SSH Key)
提示:PHP版本是很多新手踩坑的重灾区。用
php -v确认你的版本,如果低于8.2,升级或切换版本再继续,否则Laravel 11的根本跑不起来。
2.2 后端项目创建与基础配置
用Composer创建Laravel项目:
composer create-project laravel/laravel backend cd backend项目创建后,先把.env文件里的数据库配置填好:
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=fullstack_demo DB_USERNAME=root DB_PASSWORD=your_password这里有一个我一开始就踩过的坑:国内开发环境下,如果数据库是MySQL 8.0,记得检查caching_sha2_password插件是否兼容。本地调试时,我一般把.env里的数据库验证方式设为mysql_native_password,或者直接用PostgreSQL省心。
然后启动开发服务器验证:
php artisan serve浏览器打开http://localhost:8000,看到Laravel的默认页面就说明后端骨架搭好了。
2.3 前端项目创建与工程化配置
Vue 3项目推荐直接用Vite,它的冷启动速度快得让人感动:
npm create vite@latest frontend -- --template vue cd frontend npm install npm install axios vue-router@4 pinia npm install -D sass装完后把Vite的代理配置写好,让前端开发服务器把API请求转发给Laravel:
// vite.config.js export default defineConfig({ server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true } } } })这样配置后,前端页面上请求/api/xxx,Vite会把请求转发到Laravel所在的后端地址。开发环境下不会有跨域问题(因为浏览器看到的请求是同源的),后端也不用配置CORS,省掉一大半麻烦。
注意:生产环境部署时,前后端通常会分属不同域名或同域不同端口,配置CORS的时机是在部署阶段,开发阶段用代理就够了,别过早引入跨域处理增加复杂度。
3. 数据库设计与后端API开发
3.1 数据表设计:用迁移文件锁定结构
Laravel的迁移文件是数据库结构的版本控制工具,新人务必要养成“改表结构就写迁移”的习惯,千万别直接在数据库里手工改。手工改的后果是:同事拉代码后结构不一致、测试环境结构漂移、线上回滚无从下手。
以博客系统为例,设计文章表的迁移:
php artisan make:migration create_articles_table// database/migrations/xxxx_create_articles_table.php Schema::create('articles', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->string('title'); $table->text('content'); $table->string('slug')->unique(); $table->enum('status', ['draft', 'published'])->default('draft'); $table->timestamp('published_at')->nullable(); $table->timestamps(); });这里我重点解释几个细节:
foreignId('user_id')->constrained():直接生成外键约束,关联users表,避免手写外键出错的概率。slug字段加unique:文章URL别名,不允许重复,这个对SEO很关键。published_at用nullable时间戳:草稿发布的时间留空,只有正式发布才写入时间,比加一个is_published布尔值更灵活。
执行迁移:
php artisan migrate3.2 Eloquent模型与关联关系的梳理
模型是API开发的核心。以文章和用户为例:
// app/Models/Article.php class Article extends Model { protected $fillable = ['user_id', 'title', 'content', 'slug', 'status', 'published_at']; protected function casts(): array { return [ 'published_at' => 'datetime', ]; } public function user() { return $this->belongsTo(User::class); } }casts方法把时间戳自动转成Carbon实例,前端拿到的JSON是ISO 8601格式,Vue里直接用new Date()解析即可,不用额外格式化。
3.3 RESTful API的接口设计与统一响应
RESTful API的核心是资源导向。文章相关的接口设计如下:
| 方法 | 路径 | 作用 | 状态码 |
|---|---|---|---|
| GET | /api/articles | 文章列表 | 200 |
| GET | /api/articles/{id} | 文章详情 | 200 |
| POST | /api/articles | 创建文章 | 201 |
| PUT/PATCH | /api/articles/{id} | 更新文章 | 200 |
| DELETE | /api/articles/{id} | 删除文章 | 204 |
路由文件:
// routes/api.php Route::middleware('auth:sanctum')->group(function () { Route::apiResource('articles', ArticleController::class); });这里有个细节:我把文章接口全部放在auth:sanctum中间件下面,因为我的业务场景里文章创建、更新、删除都需要登录用户身份。如果要做公开接口(比如热门文章列表),就直接放在api.php的路由外面。
控制器的实现:
// app/Http/Controllers/Api/ArticleController.php public function store(StoreArticleRequest $request) { $article = $request->user()->articles()->create($request->validated()); return response()->json($article, 201); }注意看这段代码:用$request->user()直接取当前登录用户,然后通过关联关系创建文章。这样user_id根本不需要从前端传过来,既安全又省事——前端不需要也不应该知道当前用户的ID。
3.4 表单验证与异常处理
Laravel的表单请求(FormRequest)是验证逻辑的最佳归宿,不要写在控制器里一堆if判断。新建一个验证规则:
php artisan make:request StoreArticleRequest// app/Http/Requests/StoreArticleRequest.php public function authorize(): bool { return true; // 已经由 auth:sanctum 中间件保证登录 } public function rules(): array { return [ 'title' => 'required|string|max:120', 'content' => 'required|string|min:10', 'slug' => 'required|string|unique:articles,slug', 'status' => 'required|in:draft,published', ]; }验证失败时,Laravel会自动返回422状态码和错误信息,前端axios拦截器里统一处理即可。
后端API的响应结构建议统一,我在项目中固定使用如下格式:
{ "data": {}, "message": "操作成功", "status": 200 }如果异常发生,则:
{ "data": null, "message": "文章不存在", "status": 404 }通过app/Exceptions/Handler.php或Laravel 11的bootstrap/app.php里的异常映射统一处理。统一的响应格式是前后端联调顺畅的基石,省去了“这个接口为什么返回结构不一样”的排查。
4. 认证鉴权:从登录到权限管控
4.1 Sanctum还是Passport:按场景选型
Laravel官方的认证扩展有两个:Passport(基于OAuth2)和Sanctum(基于Token)。我的选择标准很简单:
- 项目是单页应用、移动端API、内部系统,直接用Sanctum,轻量、配置简单,一个Token解决。
- 项目要开放第三方登录、多端授权、刷新令牌、复杂OAuth2场景,才需要Passport。
绝大多数全栈项目用Sanctum就够了。Laravel 11自带Sanctum支持,安装只要一条命令:
php artisan install:api这条命令会把Sanctum的配置和迁移一次性搞定。
4.2 Token认证的实现流程
先建一个登录接口:
// routes/api.php Route::post('/register', [AuthController::class, 'register']); Route::post('/login', [AuthController::class, 'login']); Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:sanctum');登录控制器的核心逻辑:
public function login(LoginRequest $request) { if (!Auth::attempt($request->only('email', 'password'))) { return response()->json([ 'message' => '邮箱或密码错误' ], 401); } $user = Auth::user(); $token = $user->createToken('web')->plainTextToken; return response()->json([ 'data' => [ 'user' => $user, 'token' => $token, ], 'message' => '登录成功' ]); }createToken是Sanctum提供的,返回的Token在数据库personal_access_tokens表里存一份哈希,同时给前端返回明文Token。前端拿到Token后存到localStorage或Pinia里,后续请求通过Authorization: Bearer <token>头携带。
登出时:
public function logout(Request $request) { $request->user()->currentAccessToken()->delete(); return response()->json(['message' => '已退出登录']); }注意:很多人忽略登出时删除Token,导致Token长期有效,一旦泄露后患无穷。一定要在登出接口里调用
delete(),让Token失效。
4.3 基于Gate和Policy的细粒度权限
全栈开发里,认证(你是谁)和授权(你能干什么)是两件事。文章的编辑删除不应该只靠sanctum中间件挡住未登录用户,还需要判断“你只能改自己的文章”。
生成Policy:
php artisan make:policy ArticlePolicy --model=Article// app/Policies/ArticlePolicy.php public function update(User $user, Article $article): bool { return $user->id === $article->user_id; } public function delete(User $user, Article $article): bool { return $user->id === $article->user_id; }控制器里使用:
public function update(UpdateArticleRequest $request, Article $article) { $this->authorize('update', $article); $article->update($request->validated()); return response()->json($article); }如果用户没有权限,Laravel自动返回403。这一层保护比前端隐藏编辑按钮要安全得多——前端只是交互层的修饰,真正的安全防线在后端的每一步,这个原则全栈开发者要牢记。
5. 前端Vue项目搭建与API对接
5.1 Vue3工程结构与核心依赖
Vue3项目我习惯按模块划分目录,不要所有组件都堆在components里:
frontend/src/ ├── api/ # API请求模块(按资源拆分) │ ├── auth.js │ ├── article.js │ └── http.js # axios实例 ├── assets/ # 静态资源 ├── components/ # 通用组件 ├── router/ # 路由配置 ├── store/ # Pinia状态 ├── views/ # 页面组件 └── App.vueVue3组合式API是默认选择,用<script setup>写法,代码更简洁,也不需要export default那一堆配置。这个写法从Vue3.2开始就是官方推荐,别再抱着选项式API不放了。
5.2 Axios封装与请求拦截
API调用的核心是axios实例的封装。我们把baseURL、超时时间、请求拦截器、响应拦截器统一管理:
// src/api/http.js import axios from 'axios' import { useAuthStore } from '@/store/auth' import router from '@/router' const http = axios.create({ baseURL: '/api', timeout: 15000 }) // 请求拦截器:自动携带Token http.interceptors.request.use(config => { const authStore = useAuthStore() if (authStore.token) { config.headers.Authorization = `Bearer ${authStore.token}` } return config }) // 响应拦截器:统一处理错误 http.interceptors.response.use( response => response.data, error => { if (error.response?.status === 401) { const authStore = useAuthStore() authStore.logout() router.push('/login') } return Promise.reject(error) } ) export default http这段代码有三点经验值得说:
- 请求拦截器从Pinia里读Token,而不是从localStorage每次取,因为Pinia的内存访问比localStorage快得多,而且能取到响应式状态。
- 响应拦截器中,拿到
response.data直接返回,这样业务代码里不需要写res.data.data这种层层取值的恶心代码。 - 401状态码在拦截器里统一处理,跳登出逻辑,页面组件压根不用关心登录态过期的问题。
5.3 Pinia状态管理与登录态保持
Pinia是Vue3官方推荐的状态管理库。登录态相关的store长这样:
// src/store/auth.js import { defineStore } from 'pinia' export const useAuthStore = defineStore('auth', { state: () => ({ token: localStorage.getItem('token') || '', user: null }), actions: { async login(credentials) { const data = await http.post('/login', credentials) this.token = data.data.token this.user = data.data.user localStorage.setItem('token', this.token) }, async logout() { await http.post('/logout') this.token = '' this.user = null localStorage.removeItem('token') } } })这里有一个完整方案里必须处理的细节:页面刷新后Pinia的state会重置,所以state里初始化Token时要从localStorage里读一次,这样刷新页面后登录态不会丢。如果用户信息也要持久化,建议在main.js启动时调一次获取当前用户信息的接口把user重新拉回来,避免直接存到localStorage里造成XSS风险。
5.4 路由守卫与页面权限控制
前端路由需要和登录态联动,用Vue Router的全局前置守卫:
// src/router/index.js router.beforeEach((to, from, next) => { const authStore = useAuthStore() if (to.meta.requiresAuth && !authStore.token) { next({ path: '/login', query: { redirect: to.fullPath } }) } else { next() } })同时在路由表里标出哪些页面需要登录:
{ path: '/admin', component: AdminLayout, meta: { requiresAuth: true }, children: [ { path: 'articles', component: ArticleManage }, ] }这个守卫的逻辑很简单:去受保护页面时发现没登录,就直接踢到登录页,并且带上一个redirect参数,登录成功后再跳回原目标页。这个小细节很多人会忽略,但用户体感差别很大——登录完又回到首页,用户还得重新找路径,体验就很差。
6. 跨域问题的处理与调试实战
6.1 CORS的完整解法
开发环境下用Vite代理已经规避了跨域,但部署到生产环境时,前端地址(比如https://app.example.com)和后端API地址(比如https://api.example.com)不同,跨域就来了。
Laravel处理CORS是内置支持的,配置在config/cors.php。部署场景下的典型配置:
'paths' => ['api/*', 'sanctum/csrf-cookie'], 'allowed_methods' => ['*'], 'allowed_origins' => [env('FRONTEND_URL', 'https://app.example.com')], 'allowed_headers' => ['*'], 'supports_credentials' => true,特别提醒:supports_credentials要用true,因为Sanctum的Token认证依赖Cookie时(如果用了SPA模式)需要允许携带凭证。如果前端用Authorization Header方式传Token,不一定需要Cookie,但保险起见还是打开。
注意:
allowed_origins不要用*配合true,浏览器规范不允许带凭证的跨域请求用通配符源,Nginx的日志会给你刷一屏CORS错误。固定到你自己的前端域名即可。
6.2 开发环境代理踩坑记录
Vite代理配置里最容易踩的坑是:前端请求路径写了绝对地址http://backend:8000/api/login,绕过了Vite代理,这样请求到后端会跨域;或者代理只配置了/api前缀,但偶尔有个接口你写的是/uploads,结果代理完全不生效。
我建议把代理配置按下面的规则写全:
proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true }, '/storage': { target: 'http://localhost:8000', changeOrigin: true } }图片上传的前端地址通常是/storage/xxx.jpg,这个路径在Laravel里是公开目录,但跑到前端服务器上就是404,加上代理转发到后端就能正常显示。
6.3 环境变量管理的几个小习惯
全栈项目里,前端和后端都有各自的环境变量体系:
- 后端在
.env里管数据库连接、JWT密钥、第三方应用密钥。 - 前端在
.env.development和.env.production里管API地址、调试开关。
环境变量管理有两个小习惯值得养成:
第一,.env不要提交到Git仓库。准备一份.env.example给团队,里面用占位符标明需要填什么。
第二,前端环境变量名必须以VITE_开头。这是Vite的约定,比如:
VITE_API_BASE_URL=https://api.example.com/api否则你在前端代码里读不到它,这个小坑又会让新人莫名其妙卡半天。
7. 自动化测试与部署上线
7.1 后端接口测试:让回归变成体力活
全栈项目的后端最好从一开始就写测试。Laravel内置的测试工具基于PHPUnit,写一个接口测试很快:
php artisan make:test ArticleApiTest// tests/Feature/ArticleApiTest.php public function test_user_can_create_article(): void { $user = User::factory()->create(); Sanctum::actingAs($user); $response = $this->postJson('/api/articles', [ 'title' => '第一篇测试文章', 'content' => '内容足够长,满足最小长度要求', 'slug' => 'first-test-article', 'status' => 'published', ]); $response->assertStatus(201) ->assertJsonPath('data.title', '第一篇测试文章'); $this->assertDatabaseHas('articles', ['slug' => 'first-test-article']); }Sanctum::actingAs($user)是Sanctum提供的“假装已登录”机制,测试里不用真的走一遍登录流程拿Token,直接模拟持有Token的状态。跑一下测试:
php artisan test这套测试跑起来之后,你会发现后端的回归测试变成了体力活。之前改一个字段要手动开Postman测半天,现在一条命令几十个用例全跑完,安全感完全不一样。
7.2 前端组件测试的轻量实践
前端测试我用的是Vitest + Vue Test Utils。对于组件测试,我的原则是只测关键逻辑,不测UI快照。比如文章编辑表单的“未填标题时不能提交”:
// src/components/__tests__/ArticleForm.spec.js import { mount } from '@vue/test-utils' import ArticleForm from '@/components/ArticleForm.vue' test('标题为空时提交按钮禁用', async () => { const wrapper = mount(ArticleForm) const submitBtn = wrapper.find('button[type="submit"]') expect(submitBtn.attributes('disabled')).toBeDefined() })组件测试的目标不是追求覆盖率100%,而是让关键逻辑在重构时不至于被悄悄改坏。UI细节的还原交给人工验收,逻辑层的保障交给自动化测试,这样投入产出比最高。
7.3 部署上线的完整流程
部署方案我用的是“Nginx + PHP-FPM + pm2”这套经典组合。
后端部署步骤:
- 服务器安装PHP 8.2、Nginx、MySQL、Composer。
- 拉取代码到
/var/www/backend。 - 复制
.env并配置好数据库和生产环境变量。 - 安装依赖:
composer install --optimize-autoloader --no-dev- 执行迁移:
php artisan migrate --force- 缓存路由和配置:
php artisan config:cache php artisan route:cache php artisan view:cache前端部署步骤:
- 生产构建:
npm run build- 把
dist/目录同步到Nginx的Web目录,比如/var/www/frontend/dist。
Nginx配置核心要点:
# 前端 server { listen 80; server_name app.example.com; root /var/www/frontend/dist; index index.html; location / { try_files $uri $uri/ /index.html; } } # 后端 server { listen 80; server_name api.example.com; root /var/www/backend/public; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }前端的try_files配置是SPA部署最常见的坑。没写这行的后果是:用户直接访问https://app.example.com/articles时Nginx找不到articles这个文件,直接404。写了try_files ... /index.html后,所有路由都交给前端路由去处理,刷新页面才能正常渲染。
8. 常见问题与排查技巧实录
8.1 高频问题速查表
结合我自己的经验,把全栈开发中最常见的问题整理成一份速查表:
Q1:接口返回401,明明已经在Header里带了Token?检查一下请求是否真的走了axios拦截器。如果你直接用fetch或者另一个没经过封装的axios实例发请求,Token自然不会被带上。
Q2:前端能拿到数据,但页面不刷新?大概率是响应式对象漏了。检查你Pinia里的操作是否用了this来访问,Vue3响应式对直接state.xxx =赋值是生效的,但如果操作的是数组或嵌套对象,需要注意替换整个对象而不是修改内部属性。
Q3:php artisan migrate一直报连接错误?先php artisan config:clear清掉配置缓存,再看.env里的数据库配置是否写对。有时候你改了.env但之前跑过config:cache,改动了配置却不生效,清缓存就好。
Q4:PUT请求传表单数据,Laravel接收不到?检查前端Content-Type是否设置成application/json,以及是否用了axios.put(url, data)的形式。axios会把对象自动序列化成JSON,但如果你手动拼接了FormData,行为就不一样了。
Q5:Nginx上访问API总返回502?大概率是PHP-FPM没跑起来,或者fastcgi_pass的Socket路径不对。systemctl status php8.2-fpm先看进程,再查/var/run/php/目录下的Socket文件名。
Q6:上传图片后前端访问不到?Laravel的storage目录并不是Web根目录下的公开路径,需要先执行:
php artisan storage:link把storage/app/public软链接到public/storage,否则前端根本找不到文件。
8.2 我踩过的三个最有价值的坑
第一个坑是配置缓存导致的环境变量不生效。有一次改完.env重新部署后发现生产环境依然连的是旧数据库,排查半天发现Nginx的PHP-FPM进程池里还缓存着旧的.env。后来我养成了习惯:每次部署完一定php artisan config:cache,但必须在确认.env正确之后。清缓存、改配置、重新缓存,这个顺序一步都不能乱。
第二个坑是Vue路由的history mode和Nginx冲突。开发环境一切正常,部署上线后直接访问某个URL就是404。解决方式上面说过了,location / { try_files $uri $uri/ /index.html; },但这行配置对很多熟悉纯PHP部署的运维来说特别容易被漏掉。每次部署前检查这一行,已经变成我的固定动作了。
第三个坑是Token过期时间设置太短导致的用户反复掉线。Sanctum默认Token有效期为24小时,我把过期时间改成7天后:
// app/Providers/AuthServiceProvider.php public function boot(): void { Sanctum::expiration(now()->addDays(7)); }这个改动效果立竿见影,用户不再每天被踢出登录态了。
8.3 全栈调试工具组合推荐
最后分享几个大幅提升调试效率的工具:
- 浏览器Network面板:Interface调试的第一站。看到请求状态码、响应时间、载荷,80%的问题当场就能定位。
- Laravel Telescope:Laravel的调试助手,请求、SQL、日志都集中在后台面板里,渲染异常的接口到底哪一步慢一目了然。安装后记得设为仅本地环境启用。
- vue-devtools:组件状态、Pinia store、事件流都可视化,响应式数据出问题时的第一个排查工具。
- Postman或Apifox:后端接口单独调试的首选,接口没写好之前先别让前端来等。
提示:一套稳定的工具链本身也能减少焦虑。调试是开发过程中最自然的事,别指望一次就能写出完美无误的代码,用工具把问题暴露出来,再一个个解决,全栈开发就是一个不断缩短“发现问题到解决问题”之间距离的过程。
我个人这几年的体验是,全栈开发最需要的不是某项单一技术玩得有多深,而是理解数据从头到尾怎么流转:用户在页面上点了一个按钮,数据怎么通过HTTP到后端,后端怎么验证和存储,然后又怎么返回前端渲染成新的界面。Vue+Laravel这套组合恰好把这条链路里的每一环都安排得相对舒适,框架替你把大部分繁琐事处理掉了,你才有余力去关注业务本身的逻辑和体验。后端的安全底线、前端的交互细节、部署环境的稳定性,这三样都顾到了,才算一个真正能交付的全栈项目。