Livewire 3 与 Volt 组件化开发
下面是 Livewire3与 Volt 组件化开发的完整指南。 --- Livewire3与 Volt 组件化开发 ↓ s)────────────────────────────── 一、安装composerrequire livewire/livewirecomposerrequire livewire/volt php artisan volt:install 在布局文件中引入资源Livewire3自动包含 Alpine.js!-- resources/views/layouts/app.blade.php --!DOCTYPE htmlhtmlheadlivewireStyles/headbody{{$slot}}livewireScripts/body/html--- 二、Livewire3标准组件 类组件结构?php // app/Livewire/PostList.php namespace App\Livewire;use App\Models\Post;use Livewire\Attributes\Computed;use Livewire\Attributes\Lazy;use Livewire\Attributes\On;use Livewire\Attributes\Url;use Livewire\Component;use Livewire\WithPagination;class PostList extends Component{use WithPagination;#[Url] // 同步到 URL query stringpublic string$search;public string$sortBycreated_at;// 计算属性自动缓存避免重复查询#[Computed]publicfunctionposts(){returnPost::query()-when($this-search, fn($q)$q-where(title,like,%{$this-search}%))-orderBy($this-sortBy)-paginate(10);}// 监听其他组件派发的事件#[On(post-created)]publicfunctionrefresh(): void{unset($this-posts);// 清除计算属性缓存}publicfunctiondelete(int$id): void{Post::findOrFail($id)-delete();unset($this-posts);$this-dispatch(post-deleted);}publicfunctionrender(){returnview(livewire.post-list);}}!-- resources/views/livewire/post-list.blade.php --divinput wire:model.live.debounce.300mssearchplaceholder搜索.../foreach($this-posts as$post)div wire:key{{$post-id }}h3{{$post-title}}/h3button wire:clickdelete({{$post-id }})wire:confirm确认删除删除/button/divendforeach{{$this-posts-links()}}/div--- 三、Volt 单文件组件 Volt 让 PHP 逻辑和 Blade 模板共存于一个文件有两种 API 风格。 函数式 API推荐?php // resources/views/livewire/counter.blade.php usefunctionLivewire\Volt\{state, computed, action, on, mount};// 声明响应式状态 state([count0,step1]);// 生命周期钩子 mount(function(){$this-countcache(counter,0);});// 计算属性$doubledcomputed(fn()$this-count *2);// Action$incrementaction(function(){$this-count$this-step;cache([counter$this-count]);});$decrementaction(function(){$this-countmax(0,$this-count -$this-step);});// 监听事件 on([reset-counterfunction(){$this-count0;}]);?divp计数{{$count}}双倍{{$this-doubled}}/pinput wire:modelsteptypenumbermin1/button wire:clickdecrement-/buttonbutton wire:clickincrement/button/div类 API复杂逻辑时使用?php // resources/views/livewire/create-post.blade.php use App\Models\Post;use Livewire\Attributes\Validate;use Livewire\Volt\Component;new class extends Component{#[Validate(required|min:3)]public string$title;#[Validate(required|min:10)]public string$body;publicfunctionsave(): void{$this-validate();Post::create([title$this-title,body$this-body,user_idauth()-id(),]);$this-dispatch(post-created);$this-reset(title,body);session()-flash(message,发布成功);}}?divif(session(message))pclasstext-green-600{{session(message)}}/pendifform wire:submitsaveinput wire:modeltitleplaceholder标题/error(title)spanclasstext-red-500{{$message}}/spanenderrortextarea wire:modelbodyplaceholder内容/textareaerror(body)spanclasstext-red-500{{$message}}/spanenderrorbuttontypesubmitwire:loading.attrdisabledspan wire:loading wire:targetsave保存中.../spanspan wire:loading.remove发布/span/button/form/div--- 四、生命周期钩子 // Volt 函数式 API 中的完整生命周期 usefunctionLivewire\Volt\{mount, hydrate, dehydrate, updating, updated};mount(function(int$postId){$this-postPost::find($postId);});// 每次请求开始时反序列化后 hydrate(function(){// 重建非序列化状态});// 每次请求结束前序列化前 dehydrate(function(){// 清理临时状态});// 属性更新前 updating(function(string$name, mixed$value){if($namestatus!in_array($value,[draft,published])){$this-addError(status,无效状态);}});// 属性更新后可针对特定属性 updated([searchfunction(){$this-resetPage();}]);--- 五、表单对象Form Objects?php // app/Livewire/Forms/PostForm.php namespace App\Livewire\Forms;use App\Models\Post;use Livewire\Attributes\Validate;use Livewire\Form;class PostForm extends Form{public ?Post$postnull;#[Validate(required|min:3|max:255)]public string$title;#[Validate(required|min:10)]public string$body;#[Validate(required|in:draft,published)]public string$statusdraft;publicfunctionsetPost(Post$post): void{$this-post$post;$this-title$post-title;$this-body$post-body;$this-status$post-status;}publicfunctionstore(): Post{$this-validate();returnPost::create($this-only(title,body,status));}publicfunctionupdate(): void{$this-validate();$this-post-update($this-only(title,body,status));}}// 在 Volt 组件中使用 Form Object use App\Livewire\Forms\PostForm;usefunctionLivewire\Volt\{form, action};form(PostForm::class,form);$saveaction(function(){$this-form-store();$this-dispatch(post-created);$this-form-reset();});--- 六、Alpine.js 深度集成!--$wire对象在 Alpine 中直接操作 Livewire 状态 --div x-data{ open: false }!-- entangle双向绑定 Livewire 属性到 Alpine --div x-data{ search:$wire.entangle(search).live }input x-modelsearch//div!-- 调用 Livewire 方法 --button click$wire.delete({{$post-id }})删除/button!-- 监听 Livewire 事件 --div x-on:post-created.windowopen false;$wire.$refresh().../div!-- 读取 Livewire 属性 --span x-text$wire.count/span/div!-- 加载状态管理 --button wire:clicksavesvg wire:loading wire:targetsaveclassanimate-spin.../svgspan wire:loading.remove wire:targetsave保存/span/button!-- 针对特定元素的加载状态 --div wire:loading.classopacity-50wire:targetsearch!-- 搜索结果列表 --/div--- 七、懒加载与延迟加载 // 懒加载组件在视口内才渲染#[Lazy]class HeavyChart extends Component{// placeholder()方法定义加载占位符 publicfunctionplaceholder(){returnHTMLdivclassanimate-pulse h-64 bg-gray-200 rounded/divHTML;}publicfunctionrender(){returnview(livewire.heavy-chart);}}!-- 在模板中使用 --livewire:heavy-chart lazy /!-- 带占位符的懒加载 --livewire:heavy-chart lazyx-slot:placeholderdivclassskeleton h-64/div/x-slot:placeholder/livewire:heavy-chart// Volt 中的懒加载 usefunctionLivewire\Volt\{lazy};lazy();// 声明该组件为懒加载 --- 八、wire:navigateSPA 导航!-- 开启 SPA 模式导航无需整页刷新 --navahref/postswire:navigate文章列表/aahref/posts/createwire:navigate新建文章/a/nav!-- 预加载鼠标悬停时预取页面 --ahref/postswire:navigate.hover文章列表/a在 AppServiceProvider 中全局启用 use Livewire\Livewire;Livewire::setUpdateRoute(fn($handle)Route::post(/livewire/update,$handle));--- 九、测试?php // tests/Feature/Livewire/PostListTest.php namespace Tests\Feature\Livewire;use App\Livewire\PostList;use App\Models\Post;use App\Models\User;use Livewire\Livewire;use Tests\TestCase;class PostListTest extends TestCase{publicfunctiontest_component_renders(): void{Livewire::test(PostList::class)-assertStatus(200)-assertSee(搜索);}publicfunctiontest_search_filters_posts(): void{Post::factory()-create([titleLaravel 教程]);Post::factory()-create([titleVue.js 入门]);Livewire::test(PostList::class)-set(search,Laravel)-assertSee(Laravel 教程)-assertDontSee(Vue.js 入门);}publicfunctiontest_delete_post(): void{$postPost::factory()-create();Livewire::actingAs(User::factory()-create())-test(PostList::class)-call(delete,$post-id)-assertDispatched(post-deleted);$this-assertModelMissing($post);}// 测试 Volt 组件 publicfunctiontest_volt_counter(): void{Livewire::test(counter)// 用视图路径 -assertSee(0)-call(increment)-assertSee(1);}}--- 十、路由注册 Volt 组件 // routes/web.php use Livewire\Volt\Volt;// 直接将 Volt 组件映射到路由 Volt::route(/posts,posts.index);Volt::route(/posts/create,posts.create);Volt::route(/posts/{post},posts.show);// 带中间件 Volt::route(/dashboard,dashboard)-middleware([auth,verified]);--- 关键选型建议 ┌──────────────────────┬────────────────────────────────┐ │ 场景 │ 推荐 │ ├──────────────────────┼────────────────────────────────┤ │ 简单表单、小组件 │ Volt 函数式 API │ ├──────────────────────┼────────────────────────────────┤ │ 复杂业务逻辑、多方法 │ Livewire 类组件 │ ├──────────────────────┼────────────────────────────────┤ │ 表单验证复用 │ Form Objects │ ├──────────────────────┼────────────────────────────────┤ │ 重数据组件 │#[Lazy] #[Computed] │├──────────────────────┼────────────────────────────────┤ │ 跨组件通信 │ dispatch()#[On] │├──────────────────────┼────────────────────────────────┤ │ 实时搜索 │ wire:model.live.debounce.300ms │ └──────────────────────┴────────────────────────────────┘