Skip to content

生命周期钩子

Livewire 提供了各种生命周期钩子,允许你在组件生命周期的特定点执行代码。这些钩子使你能够在特定事件之前或之后执行操作,例如初始化组件、更新属性或渲染模板。

以下是所有可用组件生命周期钩子的列表:

钩子方法描述
mount()当组件被创建时调用
hydrate()当组件在后续请求开始时被重新水化时调用
boot()在每个请求开始时调用。包括初始请求和后续请求
updating()在更新组件属性之前调用
updated()在更新属性之后调用
rendering()在调用 render() 之前调用
rendered()在调用 render() 之后调用
dehydrate()在每个组件请求结束时调用
exception($e, $stopPropagation)当抛出异常时调用

Mount

在标准 PHP 类中,构造函数 (__construct()) 接收外部参数并初始化对象的状态。然而,在 Livewire 中,你使用 mount() 方法来接收参数并初始化组件的状态。

Livewire 组件不使用 __construct(),因为 Livewire 组件在后续网络请求中会被_重新构造_,而我们只想在组件首次创建时初始化它一次。

以下是使用 mount() 方法初始化 profile.edit 组件的 nameemail 属性的示例:

php
<?php // resources/views/components/profile/⚡edit.blade.php

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

new class extends Component {
    public $name;

    public $email;

    public function mount()
    {
        $this->name = Auth::user()->name;

        $this->email = Auth::user()->email;
    }

    // ...
};

如前所述,mount() 方法将传递给组件的数据作为方法参数接收:

php
<?php // resources/views/components/post/⚡edit.blade.php

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

new class extends Component {
    public $title;

    public $content;

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

        $this->content = $post->content;
    }

    // ...
};

你可以在所有钩子方法中使用依赖注入

Livewire 允许你通过在生命周期钩子上对方法参数进行类型提示来从 Laravel 的服务容器解析依赖。

mount() 方法是使用 Livewire 的关键部分。以下文档提供了使用 mount() 方法完成常见任务的更多示例:

Boot

尽管 mount() 很有用,但它每个组件生命周期只运行一次,你可能想在每次向服务器发送给定组件的请求开始时运行逻辑。

对于这些情况,Livewire 提供了一个 boot() 方法,你可以在其中编写每次组件类被启动时都打算运行的组件设置代码:包括初始化和后续请求。

boot() 方法对于初始化受保护属性等事情很有用,这些属性在请求之间不会被持久化。以下是将受保护属性初始化为 Eloquent 模型的示例:

php
<?php // resources/views/components/post/⚡show.blade.php

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

new class extends Component {
    #[Locked]
    public $postId = 1;

    protected Post $post;

    public function boot()
    {
        $this->post = Post::find($this->postId);
    }

    // ...
};

你可以使用此技术完全控制 Livewire 组件中组件属性的初始化。

大多数时候,你可以改用计算属性

上面使用的技术很强大;然而,通常最好使用 Livewire 的计算属性来解决这个用例。

始终锁定敏感的公共属性

如你在上面看到的,我们在 $postId 属性上使用了 #[Locked] 属性。在上述场景中,你希望确保 $postId 属性不会被客户端的用户篡改,重要的是在使用之前授权属性的值或在属性上添加 #[Locked] 以确保它永远不会被更改。

有关更多信息,请查阅关于 Locked 属性的文档

Update

客户端用户可以通过多种不同的方式更新公共属性,最常见的是通过修改带有 wire:model 的输入。

Livewire 提供了便捷的钩子来拦截公共属性的更新,以便你可以在设置值之前验证或授权它,或确保属性以给定格式设置。

以下是使用 updating 阻止修改 $postId 属性的示例。

值得注意的是,对于这个特定示例,在实际应用程序中,你应该像上面的示例一样使用 #[Locked] 属性

php
<?php // resources/views/components/post/⚡show.blade.php

use Exception;
use Livewire\Component;

new class extends Component {
    public $postId = 1;

    public function updating($property, $value)
    {
        // $property: 当前正在更新的属性名称
        // $value: 即将设置给属性的值

        if ($property === 'postId') {
            throw new Exception;
        }
    }

    // ...
};

上面的 updating() 方法在属性更新之前运行,允许你捕获无效输入并阻止属性更新。以下是使用 updated() 确保属性值保持一致的示例:

php
<?php // resources/views/components/user/⚡create.blade.php

use Livewire\Component;

new class extends Component {
    public $username = '';

    public $email = '';

    public function updated($property)
    {
        // $property: 刚刚更新的属性名称

        if ($property === 'username') {
            $this->username = strtolower($this->username);
        }
    }

    // ...
};

现在,每当 $username 属性在客户端更新时,我们将确保值始终是小写的。

因为在使用更新钩子时你通常针对特定属性,Livewire 允许你直接将属性名称指定为方法名称的一部分。以下是使用此技术重写的上述示例:

php
<?php // resources/views/components/user/⚡create.blade.php

use Livewire\Component;

new class extends Component {
    public $username = '';

    public $email = '';

    public function updatedUsername()
    {
        $this->username = strtolower($this->username);
    }

    // ...
};

当然,你也可以将此技术应用于 updating 钩子。

数组

数组属性有一个额外的 $key 参数传递给这些函数,以指定正在更改的元素。

请注意,当数组本身被更新而不是特定键时,$key 参数为 null。

php
<?php // resources/views/components/preferences/⚡edit.blade.php

use Livewire\Component;

new class extends Component {
    public $preferences = [];

    public function updatedPreferences($value, $key)
    {
        // $value = 'dark'
        // $key   = 'theme'
    }

    // ...
};

Hydrate 和 Dehydrate

Hydrate 和 dehydrate 是较少为人知和较少使用的钩子。然而,在特定场景中它们可以非常强大。

术语"dehydrate"和"hydrate"指的是 Livewire 组件被序列化为 JSON 用于客户端,然后在后续请求中从 JSON 反序列化回 PHP 对象的过程。

我们经常在 Livewire 的代码库和文档中使用术语"hydrate"和"dehydrate"来指代这个过程。如果你想更清楚地了解这些术语,可以查阅我们的水化文档来了解更多。

让我们看一个同时使用 mount()hydrate()dehydrate() 的示例,以支持使用自定义数据传输对象 (DTO) 而不是 Eloquent 模型来存储组件中的帖子数据:

php
<?php // resources/views/components/post/⚡show.blade.php

use Livewire\Component;

new class extends Component {
    public $post;

    public function mount($title, $content)
    {
        // 在第一个初始请求开始时运行...

        $this->post = new PostDto([
            'title' => $title,
            'content' => $content,
        ]);
    }

    public function hydrate()
    {
        // 在每个"后续"请求开始时运行...
        // 这不会在初始请求时运行("mount"会运行)...

        $this->post = new PostDto($this->post);
    }

    public function dehydrate()
    {
        // 在每个请求结束时运行...

        $this->post = $this->post->toArray();
    }

    // ...
};

现在,从操作和组件内的其他地方,你可以访问 PostDto 对象而不是原始数据。

上面的示例主要演示了 hydrate()dehydrate() 钩子的能力和性质。然而,建议你使用 Wireables 或 Synthesizers 来代替完成此操作。

Render

如果你想钩入渲染组件 Blade 视图的过程,可以使用 rendering()rendered() 钩子:

php
<?php // resources/views/components/post/⚡index.blade.php

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

new class extends Component {
    public function render()
    {
        return $this->view([
            'post' => Post::all(),
        ]);
    }

    public function rendering($view, $data)
    {
        // 在提供的视图被渲染之前运行...
        //
        // $view: 即将被渲染的视图
        // $data: 提供给视图的数据
    }

    public function rendered($view, $html)
    {
        // 在提供的视图被渲染之后运行...
        //
        // $view: 已渲染的视图
        // $html: 最终渲染的 HTML
    }

    // ...
};

Exception

有时拦截和捕获错误可能很有帮助,例如:自定义错误消息或忽略特定类型的异常。exception() 钩子允许你做到这一点:你可以对 $error 执行检查,并使用 $stopPropagation 参数来捕获问题。 当你想停止代码的进一步执行(提前返回)时,这也解锁了强大的模式,这就是像 validate() 这样的内部方法的工作方式。

php
<?php // resources/views/components/post/⚡show.blade.php

use Livewire\Component;

new class extends Component {
    public function mount()
    {
        $this->post = Post::find($this->postId);
    }

    public function exception($e, $stopPropagation) {
        if ($e instanceof NotFoundException) {
            $this->notify('Post is not found');
            $stopPropagation();
        }
    }

    // ...
};

在 trait 中使用钩子

Trait 是跨组件重用代码或将单个组件的代码提取到专用文件的有用方式。

为了避免多个 trait 在声明生命周期钩子方法时相互冲突,Livewire 支持使用声明它们的当前 trait 的_驼峰式_名称作为前缀来命名钩子方法。

这样,你可以让多个 trait 使用相同的生命周期钩子而避免冲突的方法定义。

以下是引用名为 HasPostForm 的 trait 的组件示例:

php
<?php // resources/views/components/post/⚡create.blade.php

use Livewire\Component;

new class extends Component {
    use HasPostForm;

    // ...
};

现在这是包含所有可用前缀钩子的实际 HasPostForm trait:

php
trait HasPostForm
{
    public $title = '';

    public $content = '';

    public function mountHasPostForm()
    {
        // ...
    }

    public function hydrateHasPostForm()
    {
        // ...
    }

    public function bootHasPostForm()
    {
        // ...
    }

    public function updatingHasPostForm()
    {
        // ...
    }

    public function updatedHasPostForm()
    {
        // ...
    }

    public function renderingHasPostForm()
    {
        // ...
    }

    public function renderedHasPostForm()
    {
        // ...
    }

    public function dehydrateHasPostForm()
    {
        // ...
    }

    // ...
}

在表单对象中使用钩子

Livewire 中的表单对象支持属性更新钩子。这些钩子的工作方式类似于组件更新钩子,允许你在表单对象中的属性更改时执行操作。

以下是使用 PostForm 表单对象的组件示例:

php
<?php // resources/views/components/post/⚡create.blade.php

use Livewire\Component;

new class extends Component {
    public PostForm $form;

    // ...
};

这是包含所有可用钩子的 PostForm 表单对象:

php
namespace App\Livewire\Forms;

use Livewire\Attributes\Validate;
use Livewire\Form;

class PostForm extends Form
{
    public $title = '';

    public $tags = [];

    public function updating($property, $value)
    {
        // ...
    }

    public function updated($property, $value)
    {
        // ...
    }

    public function updatingTitle($value)
    {
        // ...
    }

    public function updatedTitle($value)
    {
        // ...
    }

    public function updatingTags($value, $key)
    {
        // ...
    }

    public function updatedTags($value, $key)
    {
        // ...
    }

    // ...
}

另请参阅

  • 属性 — 在 mount() 和 boot() 中初始化属性
  • 组件 — 了解钩子在组件创建期间何时运行
  • 页面 — 使用 mount() 接收路由参数
  • 水化 — 了解 hydrate() 和 dehydrate() 钩子