您现在的位置是:自如初>LaravelLaravel
Laravel9 & Inertia & Vue3 构建单页 SPA CURD 页面演示案例
温新
2022-08-07 23:04:00
【Laravel】
2555人已围观
简介Laravel9 & Inertia & Vue3 构建单页 SPA CURD 页面演示案例。Laravel9 已经使用 Vite 前端工具来构建应用,习惯了原有的方式时,再来使用 Vite 新工具,似乎有点不太会用了,一切都好像变了,但一切都好像又没变。一股熟悉的陌生感迎面而来。就以本篇文章作为拥抱新变化的开始吧!
hi,我是温新,一名PHPer
积极拥抱变化,不要止步不前
时间:2022-08-07
Laravel9 已经使用 Vite 前端工具来构建应用,习惯了原有的方式时,再来使用 Vite 新工具,似乎有点不太会用了,一切都好像变了,但一切都好像又没变。一股熟悉的陌生感迎面而来。就以本篇文章作为拥抱新变化的开始吧!
目标:本篇文章将使用 Laravel9
& Inertia Js
& Vue3
来构建一个 CURD
简单的 SPA
应用。
第一步:创建项目并配置数据库
1)创建项目
composer create-project laravel/laravel la9vue3
2)配置数据库
// .env
DB_DATABASE=la9vue3
DB_USERNAME=la9vue3
DB_PASSWORD=la9vue3
第二步:安装 breeze & inertia js & vue3
composer require laravel/breeze --dev
php artisan breeze:install vue
npm install && npm run dev
第三步:创建 Post 模型 迁移文件 控制器
1)创建 Post
相关文件
php artisan make:model Post -mcr
2)编写迁移文件
<?php
// database/migrations/2022_08_07_131142_create_posts_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title');
$table->string('slug')->unique();
$table->text('content');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('posts');
}
};
2)修改 Post 模型文件
<?php
// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = ['title','slug','content'];
}
3)添加路由
<?php
// routes/web.php
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
Route::get('/', function () {
return Inertia::render('Welcome', [
'canLogin' => Route::has('login'),
'canRegister' => Route::has('register'),
'laravelVersion' => Application::VERSION,
'phpVersion' => PHP_VERSION,
]);
});
Route::get('/dashboard', function () {
return Inertia::render('Dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
// 添加 posts 资源路由
Route::resource('posts', \App\Http\Controllers\PostController::class);
require __DIR__.'/auth.php';
4)编写 PostController 控制器
<?php
// app/Http/Controllers/PostController.php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Inertia\Inertia;
class PostController extends Controller
{
public function index()
{
$posts = Post::all();
return Inertia::render('Post/Index', compact('posts'));
}
public function create()
{
return Inertia::render('Post/Create');
}
public function store(Request $request)
{
$request->validate([
'title' => 'required|string|max:255',
'slug' => 'required|string|max:255',
'content' => 'required',
]);
Post::create([
'title' => $request->title,
'slug' => Str::slug($request->slug),
'content' => $request->content
]);
return redirect()->route('posts.index')->with('message', 'Post Created Successfully');
}
public function edit(Post $post)
{
return Inertia::render('Post/Edit', compact('posts'));
}
public function update(Request $request, Post $post)
{
$request->validate([
'title' => 'required|string|max:255',
'slug' => 'required|string|max:255',
'content' => 'required',
]);
$post->title = $request->title;
$post->slug = Str::slug($request->slug);
$post->content = $request->content;
$post->save();
return redirect()->route('$post.index')->with('message', 'Post Updated Successfully');
}
public function destroy(Post $post)
{
$post->delete();
return redirect()->route('posts.index')->with('message', 'Post Delete Successfully');
}
}
第四步:创建 Vue 视图文件
1)创建 Index.vue。resources/js/Pages/Posts/Index.vue
<script setup>
import BreezeButton from "@/Components/Button.vue";
import { Head, Link, useForm } from '@inertiajs/inertia-vue3';
// 接收控制器数据
const props = defineProps({
posts: {
type: Object,
default: () => ({}),
},
});
const form = useForm();
function destroy(id) {
if (confirm("Are you sure you want to Delete")) {
form.delete(route('posts.destroy', id));
}
}
</script>
<template>
<Head title="Post list" />
<BreezeAuthenticatedLayout>
<template #header>
<h2 class="text-xl font-semibold leading-tight text-gray-800">
Post List
</h2>
</template>
<div class="py-12">
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
<div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<div class="mb-2">
<Link :href="route('posts.create')">
<BreezeButton>添加文章</BreezeButton></Link>
</div>
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th scope="col" class="px-6 py-3">#</th>
<th scope="col" class="px-6 py-3">
Title
</th>
<th scope="col" class="px-6 py-3">
Slug
</th>
<th scope="col" class="px-6 py-3">
Edit
</th>
<th scope="col" class="px-6 py-3">
</th>
</tr>
</thead>
<tbody>
<tr
v-for="post in posts"
:key="post.id"
class="bg-white border-b dark:bg-gray-800 dark:border-gray-700"
>
<th
scope="row"
class="px-6 py-4 font-medium text-gray-900 dark:text-white whitespace-nowrap"
>
{{ post.id }}
</th>
<th
scope="row"
class="px-6 py-4 font-medium text-gray-900 dark:text-white whitespace-nowrap"
>
{{ post.title }}
</th>
<td class="px-6 py-4">
{{ post.slug }}
</td>
<td class="px-6 py-4">
<Link :href="route('posts.edit',post.id)"
class="px-4 py-2 text-white bg-blue-600 rounded-lg" >编辑</Link
>
</td>
<td class="px-6 py-4">
<BreezeButton
class="bg-red-700"
@click="destroy(post.id)"
>
删除
</BreezeButton>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</BreezeAuthenticatedLayout>
</template>
2)创建 Create.vue。resources/js/Pages/Posts/Create.vue
<script setup>
import BreezeButton from "@/Components/Button.vue";
import { Head, Link, useForm } from '@inertiajs/inertia-vue3';
const props = defineProps({
posts: {
type: Object,
default: () => ({}),
},
});
const form = useForm({
title: '',
slug: '',
content: '',
});
const submit = () => {
form.post(route("Posts.store"));
};
</script>
<template>
<Head title="Post Create" />
<BreezeAuthenticatedLayout>
<template #header>
<h2 class="text-xl font-semibold leading-tight text-gray-800">
添加文章
</h2>
</template>
<div class="py-12">
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
<div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<form @submit.prevent="submit">
<div class="mb-6">
<label
for="Title"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>Title</label
>
<input
type="text"
v-model="form.title"
name="title"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
placeholder=""
/>
<div
v-if="form.errors.title"
class="text-sm text-red-600"
>
{{ form.errors.title }}
</div>
</div>
<div class="mb-6">
<label
for="Slug"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>Slug</label
>
<input
type="text"
v-model="form.slug"
name="title"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
placeholder=""
/>
<div
v-if="form.errors.slug"
class="text-sm text-red-600"
>
{{ form.errors.slug }}
</div>
</div>
<div class="mb-6">
<label
for="slug"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>Content</label
>
<textarea
type="text"
v-model="form.content"
name="content"
id=""
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
></textarea>
<div
v-if="form.errors.content"
class="text-sm text-red-600"
>
{{ form.errors.content }}
</div>
</div>
<BreezeButton class="ml-4 mt-4" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
创建文章
</BreezeButton>
</form>
</div>
</div>
</div>
</div>
</BreezeAuthenticatedLayout>
</template>
3)创建 Edit.vue。resources/js/Pages/Posts/Edit.vue
<template>
<Head title="Post Edit" />
<BreezeAuthenticatedLayout>
<template #header>
<h2 class="text-xl font-semibold leading-tight text-gray-800">
编辑文章
</h2>
</template>
<div class="py-12">
<div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
<div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<form @submit.prevent="submit">
<div class="mb-6">
<label
for="Title"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>Title</label
>
<input
type="text"
v-model="form.title"
name="title"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
placeholder=""
/>
<div
v-if="form.errors.title"
class="text-sm text-red-600"
>
{{ form.errors.title }}
</div>
</div>
<div class="mb-6">
<label
for="Slug"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>Slug</label
>
<input
type="text"
v-model="form.slug"
name="title"
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
placeholder=""
/>
<div
v-if="form.errors.slug"
class="text-sm text-red-600"
>
{{ form.errors.slug }}
</div>
</div>
<div class="mb-6">
<label
for="slug"
class="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300"
>Content</label
>
<textarea
type="text"
v-model="form.content"
name="content"
id=""
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
></textarea>
<div
v-if="form.errors.content"
class="text-sm text-red-600"
>
{{ form.errors.content }}
</div>
</div>
<BreezeButton class="ml-4 mt-4" :class="{ 'opacity-25': form.processing }" :disabled="form.processing">
修改
</BreezeButton>
</form>
</div>
</div>
</div>
</div>
</BreezeAuthenticatedLayout>
</template>
<script setup>
import BreezeButton from "@/Components/Button.vue";
import { Head, Link, useForm } from '@inertiajs/inertia-vue3';
const props = defineProps({
post: {
type: Object,
default: () => ({}),
},
});
const form = useForm({
id: props.post.id,
title: props.post.title,
slug: props.post.slug,
content: props.Post.content,
});
const submit = () => {
form.put(route("posts.update", props.post.id));
};
</script>
<style scoped></style>
这一个简单的案例到这里就已经完成了。
第五步:总结
这个案例使用了 Inertia Js
& Vue3
& Tailwind CSS
,从中可以看出,应用的构建发生了巨大的变化,初次转过来时,可能会有种难度加大的感觉,除了这个种感觉外,还会有种新颖强大的感觉。
很赞哦!(13)
相关文章
- GitHub Copilot CURD 提高了效率但也存在问题
- GitHub Copilot 体验
- Laravel 10.x 使用 Tailwind CSS
- Laravel 10.x - Inertiajs 使用 composer require tightenco/ziggy 组件
- Laravel 10.x 安装 Inertiajs & Vue3
- Laravel 10 CURD 演示
- Laravel 使用 Server 层
- Linux 系统安装 Laravel 安装器 无效的解决方法
- Vue3 & Vite 中如何使用 Tailwind CSS 之 Laravel9后台实现-前后端分离演示案例(三)
- Laravel9 使用 barryvdh/laravel-dompdf 创建 PDF 文件