Skip to content
全部文档

生命周期钩子

Livewire 提供多种生命周期钩子,让你在组件生命周期的特定时刻执行代码。借助这些钩子,你可以在初始化组件、更新属性或渲染模板等事件之前或之后执行操作。

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

钩子方法说明
mount()组件创建时调用
hydrate()在后续请求开始时,组件被重新 hydrate 时调用
boot()每次请求开始时调用(包括首次请求与后续请求)
updating()更新组件属性之前调用
updated()更新属性之后调用
rendering()组件视图渲染之前调用
rendered()组件视图渲染之后调用
dehydrate()每次组件请求结束时调用
exception($e, $stopPropagation)抛出异常时调用

Mount

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

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

下面的示例使用 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;
    }

    // ...
};

TIP

所有钩子方法都可以使用依赖注入

Livewire 允许你通过对生命周期钩子方法参数做类型提示,从 Laravel 服务容器 中解析依赖。

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

Boot

尽管 mount() 很有用,但它在每个组件生命周期中只运行一次;你可能希望在某个组件每次向服务器发出请求时,都在开头执行一些逻辑。

针对这些场景,Livewire 提供了 boot() 方法,你可以在其中编写希望每次组件类启动时都运行的初始化代码:无论是首次初始化还是后续请求。

boot() 适用于初始化不会在请求之间持久化的 protected 属性等情况。下面是将 protected 属性初始化为 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() // [tl! highlight:3]
    {
        $this->post = Post::find($this->postId);
    }

    // ...
};

你可以用这种技巧,完全掌控 Livewire 组件中属性的初始化过程。

TIP

多数情况下,改用计算属性即可

上面的技巧很强大;不过对于这类场景,通常更好的做法是使用 Livewire 的计算属性

WARNING

务必锁定敏感的公共属性

如上所示,我们在 $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: The name of the current property being updated
        // $value: The value about to be set to the property

        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: The name of the current property that was updated

        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,再在后续请求中反序列化回 PHP 对象。

在 Livewire 的代码库和文档中,我们经常用「hydrate」和「dehydrate」来指代这一过程。若想更清楚地理解这些术语,可以参阅我们的 hydration 文档

来看一个同时使用 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)
    {
        // Runs at the beginning of the first initial request...

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

    public function hydrate()
    {
        // Runs at the beginning of every "subsequent" request...
        // This doesn't run on the initial request ("mount" does)...

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

    public function dehydrate()
    {
        // Runs at the end of every single request...

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

    // ...
};

现在,你可以在组件内的 action 及其他位置访问 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)
    {
        // Runs BEFORE the provided view is rendered...
        //
        // $view: The view about to be rendered
        // $data: The data provided to the view
    }

    public function rendered($view, $html)
    {
        // Runs AFTER the provided view is rendered...
        //
        // $view: The rendered view
        // $html: The final, rendered 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() // [tl! highlight:3]
    {
        $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 的 camelCase 名称作为钩子方法的前缀。

这样,多个 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() 接收路由参数
  • Hydration理解 hydrate() 与 dehydrate() 钩子