组件

未匹配的标注
本文档最新版为 2.x,旧版本可能放弃维护,推荐阅读最新版!

组件

组件是 Livewire 应用程序的最小单元。它将状态和行为结合在一起,创建出可复用的前端UI组件。这一章将详细介绍创建和渲染组件的基础知识。

创建组件

Livewire 组件就是一个继承了 Livewire\Component 的 PHP 类。您可以手动或使用以下 Artisan 命令创建组件文件:

php artisan make:livewire CreatePost

您也可以使用短横线分隔命名法创建:

php artisan make:livewire create-post

运行此命令后,Livewire 将在您的应用程序中创建两个新文件。第一个文件 app/Livewire/CreatePost.php 将是组件的类:

<?php

namespace App\Livewire;

use Livewire\Component;

class CreatePost extends Component
{
    public function render()
    {
        return view('livewire.create-post');
    }
}

第二个文件resources/views/livewire/create-post.blade.php将是组件的 Blade 视图:

<div>
    {{-- ... --}}
</div>

您可以使用命名空间语法或者点连接在子目录中创建组件。例如,以下命令将在 Posts 子目录中创建一个 CreatePost 组件:

php artisan make:livewire Posts\\CreatePost
php artisan make:livewire posts.create-post

内联组件

如果您的组件模板内容比较加单,您可以创建一个内联组件。内联组件是单文件的 Livewire 组件,其视图模板直接包含在 render()方法中,无须创建单独的视图文件:

<?php

namespace App\Livewire;

use Livewire\Component;

class CreatePost extends Component
{
    public function render()
    {
        return <<<'HTML'
        <div>
            {{-- 您的 Blade 模板在这里... --}}
        </div>
        HTML;
    }
}

您可以使用 make:livewire 命令并添加 --inline 标志来创建内联组件:

php artisan make:livewire CreatePost --inline

省略 render 方法

为了减少组件中的模板代码,您可以完全省略 render() 方法,Livewire 将使用其自己的底层 render() 方法,该方法返回与您的组件名称对应的视图:

<?php

namespace App\Livewire;

use Livewire\Component;

class CreatePost extends Component {
    //
}

如果上面的组件在页面上呈现,Livewire 将自动使用 livewire.create-post 模板进行渲染。

自定义组件模板

您可以通过运行以下命令自定义 Livewire 用于生成新组件的模板:

php artisan livewire:stubs

这将在您的应用程序中创建四个新文件:

  • stubs/livewire.stub — 用于生成新组件
  • stubs/livewire.inline.stub — 用于生成内联组件
  • stubs/livewire.test.stub — 用于生成测试文件
  • stubs/livewire.view.stub — 用于生成组件视图

尽管这些文件位于您的应用程序中,但您仍然可以使用 make:livewire Artisan 命令,Livewire 将在生成文件时自动使用您自定义的模板。

设置属性

Livewire 组件的属性可以存储数据,并且在组件类和 Blade 视图中可以轻松访问。本节介绍如何向组件添加属性并在应用程序中使用它们。要向 Livewire 组件添加属性,在组件类中声明一个公共属性即可。例如在 CreatePost 组件中创建一个$title 属性:

<?php

namespace App\Livewire;

use Livewire\Component;

class CreatePost extends Component {
    public $title = '文章标题...';

    public function render() {
        return view('livewire.create-post');
    }
}

在视图中访问属性

组件属性会自动提供给组件对应的 Blade 视图。您可以使用标准 Blade 语法引用它。下面我们将显示 $title 属性的值:

<div>
    <h1>标题:"{{ $title }}"</h1>
</div>

这个组件的渲染后输出的是:

<div>
    <h1>标题:"文章标题..."</h1>
</div>

与视图共享附加数据

除了从视图中访问属性之外,您还可以通过 render()方法将数据传递给视图,就像从控制器中传递数据一样。通常用于传递附加数据并且无需存储为属性,属性针对性能和安全进行了优化。

使用 with() 方法既可以在 render() 方法中将数据传递给视图。例如,假设您想要将文章作者传递给视图:

<?php

namespace App\Livewire;

use Illuminate\Support\Facades\Auth;
use Livewire\Component;

class CreatePost extends Component {
    public $title;

    public function render() {
        return view('livewire.create-post')->with([
            'author' => Auth::user()->name,
        ]);
    }
}

现在,您可以从组件的 Blade 视图中访问 $author 属性:

<div>
    <h1>标题:{{ $title }}</h1>

    <span>作者:{{ $author }}</span>
</div>

在 @foreach 循环中添加 wire:key

在 Livewire 模板中使用 @foreach 循环遍历数据时,您必须在循环的根元素中添加一个唯一的 wire:key 属性。

如果 Blade 循环中没有 wire:key 属性,Livewire 将无法正确将旧元素匹配到它们的新位置,当循环发生变化时,这可能会导致许多无法预料的问题。

例如循环遍历一组文章时,您可以将 wire:key 属性设置为文章的 ID:

<div>
    @foreach ($posts as $post)
        <div wire:key="{{ $post->id }}"> 
            <!-- ... -->
        </div>
    @endforeach
</div>

将输入与属性绑定

Livewire 最强大的功能之一是“数据绑定”,即自动将页面上的表单输入与组件属性同步。

使用 wire:model 指令将 CreatePost 组件的 $title 属性绑定到 Input 框:

<form>
    <label for="title">标题:</label>

    <input type="text" id="title" wire:model="title">
</form>

Input 框输入的任何更改都将自动与 Livewire 组件中的 $title 属性同步。

“为什么我在输入时组件不会实时更新?”
如果您在浏览器中尝试过这个并且对于为什么标题没有自动更新感到困惑,那是因为 Livewire 只在“操作”提交时更新组件,比如按下提交按钮,而不是在用户在字段中键入时。这有助于减少网络请求并提高性能。要在用户输入时启用“实时”更新,可以使用 wire:model.live。了解更多关于数据绑定的信息。

Livewire 属性非常强大,是一个重要的概念。欲了解更多信息,请查阅 Livewire 属性文档。

调用方法

Livewire 组件内的方法即可以用于处理用户交互也可以执行特定任务。通常会被用于响应页面上的按钮点击或表单提交。例如在 CreatePost 组件中添加一个 save 操作:

<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\Post;

class CreatePost extends Component
{
    public $title;

    public function save()
    {
        $post = Post::create([
            'title' => $this->title
        ]);

        return redirect()->to('/posts')
             ->with('status', 'Post created!');
    }

    public function render()
    {
        return view('livewire.create-post');
    }
}

渲染组件

有两种在页面上渲染 Livewire 组件的方法:

  1. 在现有的 Blade 视图中引入 Livewire 组件
  2. 直接为 Livewire 组件分配路由作为单独的页面

先介绍第一种呈渲染组件的方式,因为它比第二种方式更简单。

您可以直接使用 <livewire:component-name /> 语法将 Livewire 组件引入到 Blade 模板中:

<livewire:create-post />

如上代码片段所示,您必须使用组件名称的短横线命名风格。使用其他风格的名称(<livewire:CreatePost />)是无效的,Livewire 无法识别它。

传递数据到组件

可以直接使用组件标签属性将外部数据传递给 Livewire 组件。例如可以使用以下语法将初始值传递给 CreatePost 组件的 $title 属性:

<livewire:create-post title="初始标题" />

如果您需要传递动态值或变量到组件,可以在标签属性前添加冒号,即可在组件属性中编写 PHP 表达式:

<livewire:create-post :title="$initialTitle" />

组件内部的 mount() 生命周期钩子方法的参数即为外部传递的参数。在这种情况下,要将 $title 参数分配给属性,您可以编写如下的 mount() 方法:

<?php

namespace App\Livewire;

use Livewire\Component;

class CreatePost extends Component
{
    public $title;

    public function mount($title = null)
    {
        $this->title = $title;
    }

    // ...
}

在这个例子中,$title 属性将被初始化为值 “初始标题”。

您可以将 mount() 方法视为类的构造函数。它在组件的初始加载时运行,但不会在页面内的后续请求中运行。您可以在生命周期文档中了解更多关于 mount()和其他有用的生命周期钩子的信息。

为了减少组件中的无用代码,您还可以省略 mount() 方法,Livewire 会自动将传入值赋值给相匹配的属性:

<?php
namespace App\Livewire;

use Livewire\Component;

class CreatePost extends Component {
    public $title;
    // ...
}

这些属性默认情况下不是响应式的 如果在初始页面加载后更改外部传递的 :title="$initialValue"$title 属性不会自动更新。这是使用 Livewire 时常见的困惑点,特别是对于之前使用过像 Vue 或 React 这样的 JavaScript 框架的开发人员,他们假设这些“参数”就像这些框架中的“响应式属性”一样。但是,不用担心,Livewire 有办法可以让属性变为响应式。

完整页面组件

Livewire 组件也可以直接作为一个完整页面组件,直接分配路由。您可以使用它们来构建具有逻辑和视图的独立页面,完全封装在 Livewire 组件内部。
要创建一个完整页面组件,需要在 routes/web.php 文件中定义路由,并使用 Route::get() 方法将组件直接映射到特定 URL。例如,在路由 /posts/create 渲染 CreatePost 组件:

use App\Livewire\CreatePost;

Route::get('/posts/create', CreatePost::class);

现在,当您在浏览器中访问 /posts/create 路径时,CreatePost 组件将作为完整页面组件呈现。

布局文件

请记住,完整的页面组件会自动使用您应用程序的默认布局文件,通常定义在 resources/views/components/layouts/app.blade.php 文件中。确保您已在此目录创建了一个 Blade 文件,并包含一个 {{ $slot }} 占位符:

<!-- resources/views/components/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.0">

        <title>{{ $title ?? '页面标题' }}</title>
    </head>
    <body>
        {{ $slot }}
    </body>
</html>

全局布局配置

要在所有组件中使用自定义布局,您可以在 config/livewire.php 中的 layout 键中设置自定义布局相对于 resources/views 的路径。例如:

'layout' => 'layouts.app'

使用上述配置,Livewire 即可在 resources/views/layouts/app.blade.php 文件中呈现完整页面组件。

每个组件的布局配置

要为特定组件使用不同的布局,您可以在组件的 render()方法上放置 Livewire 的 #[Layout] 属性,并传递自定义布局相对视图的路径:

<?php
namespace App\Livewire;

use Livewire\Attributes\Layout;
use Livewire\Component;

class CreatePost extends Component {
    // ...
    #[Layout('layouts.app')] 
    public function render() {
        return view('livewire.create-post');
    }
}

也可以类声明上方使用此属性:

<?php
namespace App\Livewire;

use Livewire\Attributes\Layout;
use Livewire\Component;

#[Layout('layouts.app')] 
class CreatePost extends Component {
    // ...
}

PHP 注解属性仅支持文字值。如果需要传递动态值,或者更喜欢此替代语法,您可以在组件的 render() 方法中使用 ->layout() 链式方法:

public function render() {
    return view('livewire.create-post')
         ->layout('layouts.app');
}

设置页面标题

为每个页面分配单独的标题对用户和搜索引擎都很有帮助。
要为完整页面组件设置自定义页面标题,首先确保您的布局文件包含一个动态标题:

<head>
    <title>{{ $title ?? '页面标题' }}</title>
</head>

接下来,在 Livewire 组件的 render() 方法上方添加 #[Title] 属性,并将其传递给您的页面标题:

<?php
namespace App\Livewire;

use Livewire\Attributes\Title;
use Livewire\Component;

class CreatePost extends Component {
    // ...
    #[Title('创建文章')] 
    public function render() {
        return view('livewire.create-post');
    }
}

这将为 CreatePost Livewire 组件设置页面标题。在此示例中页面标题为 “创建文章”。
也可以在类声明上方使用此属性:

<?php
namespace App\Livewire;

use Livewire\Attributes\Title;
use Livewire\Component;

#[Title('创建文章')] 
class CreatePost extends Component {
    // ...
}

如果需要传递动态标题,例如使用组件属性的标题,您可以在组件的 render()方法中调用 ->title() 方法:

public function render() {
    return view('livewire.create-post')
         ->title('创建文章');
}

访问路由参数

在使用完整页面组件时,您可能需要在 Livewire 组件内部访问路由参数。
为了演示,首先在 routes/web.php 文件中定义一个带有参数的路由:

use App\Livewire\ShowPost;
Route::get('/posts/{id}', ShowPost::class);

定义一个带有 id 参数的路由,表示一个帖子的 ID。然后在 Livewire 组件中,通过 mount() 方法接收路由参数:

<?php
namespace App\Livewire;

use App\Models\Post;
use Livewire\Component;

class ShowPost extends Component {
    public Post $post;

    public function mount($id) {
        $this->post = Post::findOrFail($id);
    }

    public function render() {
        return view('livewire.show-post');
    }
}

在此示例中,因为参数名称 $id 匹配路由参数 {id},如果访问了 /posts/1,Livewire 则会将值 “1” 作为 $id 传递。

使用路由模型绑定

Laravel 的路由模型绑定允许您自动从路由参数解析 Eloquent 模型。在 routes/web.php 文件中定义一个带有模型参数的路由后:

use App\Livewire\ShowPost;
Route::get('/posts/{post}', ShowPost::class);

同样可以通过组件的 mount() 方法接受路由模型参数:

<?php
namespace App\Livewire;

use App\Models\Post;
use Livewire\Component;

class ShowPost extends Component {
    public Post $post;

    public function mount(Post $post) {
        $this->post = $post;
    }

    public function render() {
        return view('livewire.show-post');
    }
}

Livewire 会自动进行路由模型绑定,与之前一样,也可以通过省略 mount() 方法来减少无用代码:

<?php

namespace App\Livewire;

use Livewire\Component;
use App\Models\Post;

class ShowPost extends Component {
    public Post $post; 

    public function render() {
        return view('livewire.show-post');
    }
}

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~