laravel-fullstack
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLaravel Fullstack
Laravel 全栈开发
Blade + Alpine.js + Vue.js + TailwindCSS development for Laravel 12+.
Blade + Alpine.js + Vue.js + TailwindCSS 开发适配 Laravel 12+。
Stack Overview
技术栈概述
| Layer | Technology | Purpose |
|---|---|---|
| Views | Blade | Server-rendered templates |
| Interactivity | Alpine.js | Lightweight JS framework |
| SPA (optional) | Vue.js | Complex client-side apps |
| Styling | TailwindCSS v4 | Utility-first CSS |
| Assets | Vite | Build tool |
| 层级 | 技术 | 用途 |
|---|---|---|
| 视图层 | Blade | 服务端渲染模板 |
| 交互层 | Alpine.js | 轻量级 JS 框架 |
| 单页应用(可选) | Vue.js | 复杂客户端应用 |
| 样式层 | TailwindCSS v4 | 实用优先的 CSS 框架 |
| 资源构建 | Vite | 构建工具 |
Blade Components
Blade 组件
Anonymous Components
匿名组件
blade
{{-- resources/views/components/button.blade.php --}}
@props([
'type' => 'button',
'variant' => 'primary',
])
<button
type="{{ $type }}"
{{ $attributes->class([
'px-4 py-2 rounded-lg font-medium transition-colors',
'bg-blue-600 text-white hover:bg-blue-700' => $variant === 'primary',
'bg-gray-200 text-gray-800 hover:bg-gray-300' => $variant === 'secondary',
'bg-red-600 text-white hover:bg-red-700' => $variant === 'danger',
]) }}
>
{{ $slot }}
</button>
{{-- Usage --}}
<x-button variant="primary">Save</x-button>
<x-button variant="danger" type="submit">Delete</x-button>blade
{{-- resources/views/components/button.blade.php --}}
@props([
'type' => 'button',
'variant' => 'primary',
])
<button
type="{{ $type }}"
{{ $attributes->class([
'px-4 py-2 rounded-lg font-medium transition-colors',
'bg-blue-600 text-white hover:bg-blue-700' => $variant === 'primary',
'bg-gray-200 text-gray-800 hover:bg-gray-300' => $variant === 'secondary',
'bg-red-600 text-white hover:bg-red-700' => $variant === 'danger',
]) }}
>
{{ $slot }}
</button>
{{-- 使用示例 --}}
<x-button variant="primary">Save</x-button>
<x-button variant="danger" type="submit">Delete</x-button>Class Components
类组件
php
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
final class Alert extends Component
{
public function __construct(
public string $type = 'info',
public ?string $title = null,
) {}
public function render(): View
{
return view('components.alert');
}
public function iconClass(): string
{
return match($this->type) {
'success' => 'text-green-500',
'error' => 'text-red-500',
'warning' => 'text-yellow-500',
default => 'text-blue-500',
};
}
}php
<?php
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
final class Alert extends Component
{
public function __construct(
public string $type = 'info',
public ?string $title = null,
) {}
public function render(): View
{
return view('components.alert');
}
public function iconClass(): string
{
return match($this->type) {
'success' => 'text-green-500',
'error' => 'text-red-500',
'warning' => 'text-yellow-500',
default => 'text-blue-500',
};
}
}Layouts
布局
blade
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ $title ?? config('app.name') }}</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="bg-gray-100 dark:bg-gray-900">
<x-navbar />
<main class="container mx-auto py-8">
{{ $slot }}
</main>
<x-footer />
</body>
</html>
{{-- Usage --}}
<x-layouts.app title="Dashboard">
<h1>Welcome</h1>
</x-layouts.app>blade
{{-- resources/views/layouts/app.blade.php --}}
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ $title ?? config('app.name') }}</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="bg-gray-100 dark:bg-gray-900">
<x-navbar />
<main class="container mx-auto py-8">
{{ $slot }}
</main>
<x-footer />
</body>
</html>
{{-- 使用示例 --}}
<x-layouts.app title="Dashboard">
<h1>Welcome</h1>
</x-layouts.app>Alpine.js Patterns
Alpine.js 模式
Basic Toggle
基础切换
blade
<div x-data="{ open: false }">
<button @click="open = !open">
Toggle Menu
</button>
<div x-show="open" x-transition>
Menu content
</div>
</div>blade
<div x-data="{ open: false }">
<button @click="open = !open">
切换菜单
</button>
<div x-show="open" x-transition>
菜单内容
</div>
</div>Dropdown
下拉菜单
blade
<div x-data="{ open: false }" @click.away="open = false">
<button @click="open = !open">
Options
</button>
<div
x-show="open"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg"
>
<a href="#" class="block px-4 py-2 hover:bg-gray-100">Edit</a>
<a href="#" class="block px-4 py-2 hover:bg-gray-100">Delete</a>
</div>
</div>blade
<div x-data="{ open: false }" @click.away="open = false">
<button @click="open = !open">
选项
</button>
<div
x-show="open"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg"
>
<a href="#" class="block px-4 py-2 hover:bg-gray-100">编辑</a>
<a href="#" class="block px-4 py-2 hover:bg-gray-100">删除</a>
</div>
</div>Modal
模态框
blade
<div x-data="{ open: false }">
<button @click="open = true">Open Modal</button>
<div
x-show="open"
x-transition:enter="ease-out duration-300"
x-transition:leave="ease-in duration-200"
class="fixed inset-0 z-50 flex items-center justify-center"
>
{{-- Backdrop --}}
<div
class="fixed inset-0 bg-black/50"
@click="open = false"
></div>
{{-- Modal Content --}}
<div class="relative bg-white rounded-lg p-6 max-w-md w-full mx-4">
<h2 class="text-lg font-semibold">Modal Title</h2>
<p class="mt-2">Modal content here</p>
<div class="mt-4 flex justify-end gap-2">
<button @click="open = false">Cancel</button>
<button @click="open = false">Confirm</button>
</div>
</div>
</div>
</div>blade
<div x-data="{ open: false }">
<button @click="open = true">打开模态框</button>
<div
x-show="open"
x-transition:enter="ease-out duration-300"
x-transition:leave="ease-in duration-200"
class="fixed inset-0 z-50 flex items-center justify-center"
>
{{-- 遮罩层 --}}
<div
class="fixed inset-0 bg-black/50"
@click="open = false"
></div>
{{-- 模态框内容 --}}
<div class="relative bg-white rounded-lg p-6 max-w-md w-full mx-4">
<h2 class="text-lg font-semibold">模态框标题</h2>
<p class="mt-2">模态框内容</p>
<div class="mt-4 flex justify-end gap-2">
<button @click="open = false">取消</button>
<button @click="open = false">确认</button>
</div>
</div>
</div>
</div>Form with Loading State
带加载状态的表单
blade
<form
x-data="{ loading: false }"
@submit.prevent="loading = true; $el.submit()"
method="POST"
action="{{ route('orders.store') }}"
>
@csrf
<input type="text" name="name" required>
<button type="submit" :disabled="loading">
<span x-show="!loading">Submit</span>
<span x-show="loading">Processing...</span>
</button>
</form>blade
<form
x-data="{ loading: false }"
@submit.prevent="loading = true; $el.submit()"
method="POST"
action="{{ route('orders.store') }}"
>
@csrf
<input type="text" name="name" required>
<button type="submit" :disabled="loading">
<span x-show="!loading">提交</span>
<span x-show="loading">处理中...</span>
</button>
</form>References
参考资料
| Topic | Reference | When to Load |
|---|---|---|
| Blade advanced | references/blade.md | Slots, stacks, includes |
| Alpine.js patterns | references/alpine.md | Complex interactions |
| 主题 | 参考链接 | 适用场景 |
|---|---|---|
| Blade 高级用法 | references/blade.md | 插槽、栈、文件引入 |
| Alpine.js 模式 | references/alpine.md | 复杂交互 |
TailwindCSS v4 Quick Reference
TailwindCSS v4 速查
css
/* resources/css/app.css */
@import "tailwindcss";
@theme {
--color-primary: #3b82f6;
--color-secondary: #6b7280;
}blade
{{-- Dark mode --}}
<div class="bg-white dark:bg-gray-800">
{{-- Responsive --}}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
{{-- Transitions --}}
<button class="transition-colors duration-200 hover:bg-blue-700">css
/* resources/css/app.css */
@import "tailwindcss";
@theme {
--color-primary: #3b82f6;
--color-secondary: #6b7280;
}blade
{{-- 暗黑模式 --}}
<div class="bg-white dark:bg-gray-800">
{{-- 响应式布局 --}}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
{{-- 过渡效果 --}}
<button class="transition-colors duration-200 hover:bg-blue-700">Vite Setup
Vite 配置
js
// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
});bash
npm run dev # Development with HMR
npm run build # Production buildjs
// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
});bash
npm run dev # 开发模式(支持热模块替换)
npm run build # 生产环境构建