您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# Laravel 8 + Vue.js 如何创建 SPA 单页面应用
## 目录
1. [技术栈概述](#技术栈概述)
2. [环境准备](#环境准备)
3. [项目初始化](#项目初始化)
4. [前端架构搭建](#前端架构搭建)
5. [后端API开发](#后端api开发)
6. [前后端集成](#前后端集成)
7. [SPA路由配置](#spa路由配置)
8. [状态管理](#状态管理)
9. [认证系统实现](#认证系统实现)
10. [性能优化](#性能优化)
11. [部署上线](#部署上线)
12. [常见问题](#常见问题)
<a id="技术栈概述"></a>
## 1. 技术栈概述
### 1.1 Laravel 8 特性
- 全新的Jetstream脚手架
- 改进的路由缓存
- 模型工厂类优化
- 时间测试辅助功能
- 动态Blade组件
### 1.2 Vue.js 3.x 优势
- Composition API
- 更小的体积
- 更好的TypeScript支持
- 性能提升
### 1.3 SPA架构特点
- 无页面刷新跳转
- 前后端完全分离
- 动态数据加载
- 更接近原生应用体验
<a id="环境准备"></a>
## 2. 环境准备
### 2.1 开发环境要求
```bash
PHP >= 7.3
Node.js >= 12.x
Composer >= 2.0
NPM >= 6.0
MySQL >= 5.7
# 安装PHP扩展
sudo apt install php-mbstring php-xml php-curl
# 配置PHP.ini
memory_limit = 256M
upload_max_filesize = 50M
post_max_size = 50M
composer create-project --prefer-dist laravel/laravel laravel-vue-spa
cd laravel-vue-spa
npm install vue@next vue-router@4 vue-loader@next @vue/compiler-sfc
npm install axios vue-axios @inertiajs/inertia @inertiajs/inertia-vue3
/resources
/js
/components
/layouts
/pages
/router
/store
app.js
bootstrap.js
// resources/js/app.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
createApp(App)
.use(router)
.use(store)
.mount('#app')
<!-- resources/js/App.vue -->
<template>
<div id="app">
<nav-bar />
<router-view />
<app-footer />
</div>
</template>
<script>
import NavBar from './components/NavBar.vue'
import AppFooter from './components/AppFooter.vue'
export default {
components: { NavBar, AppFooter }
}
</script>
const mix = require('laravel-mix')
mix.js('resources/js/app.js', 'public/js')
.vue()
.sass('resources/sass/app.scss', 'public/css')
.version()
// routes/api.php
Route::group(['prefix' => 'v1'], function() {
Route::apiResource('posts', 'App\Http\Controllers\API\PostController');
Route::post('login', 'AuthController@login');
Route::post('register', 'AuthController@register');
});
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index()
{
return response()->json([
'data' => Post::paginate(10)
]);
}
}
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PostResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'content' => $this->content,
'created_at' => $this->created_at->format('Y-m-d H:i:s')
];
}
}
// resources/js/api.js
import axios from 'axios'
const api = axios.create({
baseURL: '/api/v1',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
// 请求拦截器
api.interceptors.request.use(config => {
const token = localStorage.getItem('auth_token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
export default api
// resources/js/store/modules/posts.js
import api from '../../api'
const actions = {
async fetchPosts({ commit }, page = 1) {
try {
const response = await api.get(`/posts?page=${page}`)
commit('SET_POSTS', response.data.data)
} catch (error) {
console.error(error)
}
}
}
// resources/js/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../pages/Home.vue'
import PostList from '../pages/posts/List.vue'
const routes = [
{ path: '/', component: Home },
{ path: '/posts', component: PostList },
{ path: '/:pathMatch(.*)*', redirect: '/' }
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
// routes/web.php
Route::get('/{any}', function () {
return view('app');
})->where('any', '.*');
// resources/js/store/index.js
import { createStore } from 'vuex'
import posts from './modules/posts'
import auth from './modules/auth'
export default createStore({
modules: { posts, auth }
})
// resources/js/store/modules/auth.js
const state = {
user: null,
token: localStorage.getItem('auth_token') || null
}
const mutations = {
SET_USER(state, user) {
state.user = user
},
SET_TOKEN(state, token) {
state.token = token
localStorage.setItem('auth_token', token)
}
}
export default { state, mutations }
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
// app/Http/Controllers/AuthController.php
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
$token = $request->user()->createToken('auth_token')->plainTextToken;
return response()->json(['token' => $token]);
}
return response()->json(['error' => 'Unauthorized'], 401);
}
<template>
<form @submit.prevent="handleLogin">
<input v-model="form.email" type="email">
<input v-model="form.password" type="password">
<button type="submit">Login</button>
</form>
</template>
<script>
import { reactive } from 'vue'
import { useStore } from 'vuex'
import { useRouter } from 'vue-router'
export default {
setup() {
const store = useStore()
const router = useRouter()
const form = reactive({
email: '',
password: ''
})
const handleLogin = async () => {
try {
const response = await api.post('/login', form)
store.commit('auth/SET_TOKEN', response.data.token)
router.push('/dashboard')
} catch (error) {
console.error(error)
}
}
return { form, handleLogin }
}
}
</script>
// 动态导入组件
const PostDetail = () => import('../pages/posts/Detail.vue')
// 缓存API响应
Route::get('/posts', function() {
return Cache::remember('posts', 60, function() {
return Post::all();
});
});
<img v-lazy="imageUrl" alt="description">
npm run production
php artisan config:cache
php artisan route:cache
server {
listen 80;
server_name yourdomain.com;
root /var/www/laravel-vue-spa/public;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
}
}
// 安装CORS包
composer require fruitcake/laravel-cors
// config/cors.php
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
// 在app.js中
axios.defaults.withCredentials = true;
// Vue Router中
router.beforeEach((to, from, next) => {
if (to.matched.length === 0) {
next('/404')
} else {
next()
}
})
本文详细介绍了使用Laravel 8和Vue.js 3构建SPA应用的完整流程,从环境搭建到部署上线,涵盖了前后端分离架构的核心技术点。实际开发中可根据项目需求灵活调整架构方案。 “`
注:此为精简版大纲,完整6250字版本需扩展每个章节的详细说明、代码注释、最佳实践和示意图等内容。建议补充: 1. 更多实际项目示例 2. 性能对比数据 3. 安全性考虑 4. 测试策略 5. 错误处理方案 6. 移动端适配方案等
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。