Skip to content

组件

Livewire 组件本质上是具有属性和方法的 PHP 类,可以直接从 Blade 模板调用。这种强大的组合让你能够以现代 JavaScript 替代方案的一小部分努力和复杂性来创建全栈交互式界面。

本指南涵盖了创建、渲染和组织 Livewire 组件所需了解的所有内容。你将学习可用的不同组件格式(单文件、多文件和基于类的)、如何在组件之间传递数据,以及如何将组件用作完整页面。

创建组件

你可以使用 make:livewire Artisan 命令创建组件:

shell
php artisan make:livewire post.create

这会在以下位置创建一个单文件组件:

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

blade
<?php

use Livewire\Component;

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

    public function save()
    {
        // Save logic here...
    }
};
?>

<div>
    <input wire:model="title" type="text">
    <button wire:click="save">Save Post</button>
</div>

为什么使用 ⚡ 表情符号?

你可能对文件名中的闪电符号感到好奇。这个小细节有一个实际用途:它使 Livewire 组件在编辑器的文件树和搜索结果中立即可识别。由于它是一个 Unicode 字符,它可以在所有平台上无缝工作——Windows、macOS、Linux、Git 和你的生产服务器。

表情符号完全是可选的,如果你觉得不习惯,可以在 config/livewire.php 文件中完全禁用它:

php
'make_command' => [
    'emoji' => false,
],

创建页面组件

当创建将用作完整页面的组件时,使用 pages:: 命名空间将它们组织在专用目录中:

shell
php artisan make:livewire pages::post.create

这会在 resources/views/pages/post/⚡create.blade.php 创建组件。这种组织方式使页面组件与可重用的 UI 组件清晰区分。

在下面的页面组件部分了解更多关于将组件用作页面的信息。你还可以注册自己的自定义命名空间——参见组件命名空间文档

多文件组件

随着组件或项目的增长,你可能会发现单文件方法有局限性。Livewire 提供了一个多文件替代方案,将组件拆分为单独的文件以获得更好的组织和 IDE 支持。

要创建多文件组件,传递 --mfc 标志:

shell
php artisan make:livewire post.create --mfc

这会创建一个包含所有相关文件的目录:

resources/views/components/post/⚡create/
├── create.php          # PHP 类
├── create.blade.php    # Blade 模板
├── create.js           # JavaScript(可选)
├── create.css          # 作用域样式(可选)
├── create.global.css   # 全局样式(可选)
└── create.test.php     # Pest 测试(可选,使用 --test 标志)

在格式之间转换

Livewire 提供了 livewire:convert 命令来无缝地在单文件和多文件格式之间转换组件。

自动检测并转换:

shell
php artisan livewire:convert post.create
# 单文件 → 多文件(或反之)

显式转换为多文件:

shell
php artisan livewire:convert post.create --mfc

这将解析你的单文件组件,创建目录结构,拆分文件,并删除原始文件。

显式转换为单文件:

shell
php artisan livewire:convert post.create --sfc

这会将所有文件合并回单个文件并删除目录。

转换为单文件时测试文件会被删除

如果你的多文件组件有测试文件,在转换前会提示你确认,因为测试文件无法在单文件格式中保留。

何时使用每种格式

单文件组件(默认):

  • 最适合大多数组件
  • 将相关代码放在一起
  • 一目了然
  • 适合小到中等规模的组件

多文件组件:

  • 更适合大型复杂组件
  • 改进的 IDE 支持和导航
  • 当组件有大量 JavaScript 时分离更清晰

基于类的组件:

  • 对 Livewire v2/v3 开发者熟悉
  • 传统的 Laravel 关注点分离
  • 更适合有既定约定的团队
  • 参见下面的基于类的组件

渲染组件

你可以在任何 Blade 模板中使用 <livewire:component-name /> 语法包含 Livewire 组件:

blade
<livewire:component-name />

如果组件位于子目录中,你可以使用点(.)字符表示:

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

blade
<livewire:post.create />

对于命名空间组件——如 pages::——使用命名空间前缀:

blade
<livewire:pages::post.create />

传递 Props

要向 Livewire 组件传递数据,你可以在组件标签上使用 prop 属性:

blade
<livewire:post.create title="Initial Title" />

对于动态值或变量,在属性前加冒号前缀:

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

传入组件的数据通过 mount() 方法接收:

php
<?php

use Livewire\Component;

new class extends Component {
    public $title;

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

    // ...
};

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

为了减少样板代码,你可以省略 mount() 方法,Livewire 会自动设置与传入值名称匹配的任何属性:

php
<?php

use Livewire\Component;

new class extends Component {
    public $title; // 自动从 prop 设置

    // ...
};

这些属性默认不是响应式的

如果外部的 :title="$initialValue" 在初始页面加载后改变,$title 属性不会自动更新。这是使用 Livewire 时常见的困惑点,特别是对于使用过 Vue 或 React 等 JavaScript 框架的开发者,他们会假设这些参数像那些框架中的"响应式 props"一样工作。但是,不用担心,Livewire 允许你选择使你的 props 响应式

将路由参数作为 Props 传递

当将组件用作页面时,你可以直接将路由参数传递给组件。路由参数会自动传递给 mount() 方法:

php
Route::livewire('/posts/{id}', 'pages::post.show');
php
<?php // resources/views/pages/post/⚡show.blade.php

use Livewire\Component;

new class extends Component {
    public $postId;

    public function mount($id)
    {
        $this->postId = $id;
    }
};

Livewire 也支持 Laravel 的路由模型绑定:

php
Route::livewire('/posts/{post}', 'pages::post.show');
php
<?php // resources/views/pages/post/⚡show.blade.php

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

new class extends Component {
    public Post $post; // 自动从路由绑定

    // 不需要 mount() - Livewire 自动处理
};

页面组件

组件可以使用 Route::livewire() 直接作为完整页面路由。这是 Livewire 最强大的功能之一,允许你在没有传统控制器的情况下构建完整页面。

php
Route::livewire('/posts/create', 'pages::post.create');

当用户访问 /posts/create 时,Livewire 将在你应用程序的布局文件中渲染 pages::post.create 组件。

页面组件的工作方式与常规组件相同,但它们作为完整页面渲染,可以访问:

  • 自定义布局
  • 页面标题
  • 路由参数和模型绑定
  • 布局的具名插槽

有关页面组件的完整信息,包括布局、标题和高级路由,请参阅页面文档

在视图中访问数据

Livewire 提供了多种方式将数据传递给组件的 Blade 视图。每种方法都有不同的性能和安全特性。

组件属性

最简单的方法是使用公共属性,它们在 Blade 模板中自动可用:

php
<?php

use Livewire\Component;

new class extends Component {
    public $title = 'My Post';
};
blade
<div>
    <h1>{{ $title }}</h1>
</div>

受保护的属性必须使用 $this-> 访问:

php
public $title = 'My Post';           // 可作为 {{ $title }} 访问
protected $apiKey = 'secret-key';    // 可作为 {{ $this->apiKey }} 访问

受保护的属性不会发送到客户端

与公共属性不同,受保护的属性永远不会发送到前端,用户无法操作它们。这使它们对于敏感数据是安全的。但是,它们在请求之间不会持久化,这限制了它们在大多数 Livewire 场景中的用途。它们最适合用于你不想暴露给客户端的属性声明中定义的静态值。

有关属性的完整信息,包括持久化行为和高级功能,请参阅属性文档

计算属性

计算属性是作为记忆化属性的方法。它们非常适合数据库查询等昂贵操作:

php
use Livewire\Attributes\Computed;

#[Computed]
public function posts()
{
    return Post::with('author')->latest()->get();
}
blade
<div>
    @foreach ($this->posts as $post)
        <article wire:key="{{ $post->id }}">{{ $post->title }}</article>
    @endforeach
</div>

注意 $this-> 前缀——这告诉 Livewire 调用方法并仅在当前请求期间缓存结果(不会在请求之间缓存)。更多详情,请参阅属性文档中的计算属性部分

从 render() 传递数据

类似于控制器,你可以使用 render() 方法直接将数据传递给视图:

php
public function render()
{
    return $this->view([
        'author' => Auth::user(),
        'currentTime' => now(),
    ]);
}

请记住 render() 在每次组件更新时都会运行,所以除非你需要在每次更新时获取新数据,否则避免在这里进行昂贵的操作。

组织组件

虽然 Livewire 自动在默认的 resources/views/components/ 目录中发现组件,但你可以自定义 Livewire 查找组件的位置并使用命名空间组织它们。

组件命名空间

组件命名空间允许你将组件组织到专用目录中,并使用干净的引用语法。

默认情况下,Livewire 提供两个命名空间:

  • pages:: — 指向 resources/views/pages/
  • layouts:: — 指向 resources/views/layouts/

你可以在 config/livewire.php 文件中定义额外的命名空间:

php
'component_namespaces' => [
    'layouts' => resource_path('views/layouts'),
    'pages' => resource_path('views/pages'),
    'admin' => resource_path('views/admin'),    // 自定义命名空间
    'widgets' => resource_path('views/widgets'), // 另一个自定义命名空间
],

然后在创建、渲染和路由时使用它们:

shell
php artisan make:livewire admin::users-table
blade
<livewire:admin::users-table />
php
Route::livewire('/admin/users', 'admin::users-table');

额外的组件位置

如果你想让 Livewire 在默认目录之外的额外目录中发现组件,你可以在 config/livewire.php 文件中配置它们:

php
'component_locations' => [
    resource_path('views/components'),
    resource_path('views/admin/components'),
    resource_path('views/widgets'),
],

现在 Livewire 将自动在所有这些目录中发现组件。

编程式注册

对于更动态的场景(如包开发或运行时配置),你可以在服务提供者中以编程方式注册组件、位置和命名空间:

注册单个组件:

php
use Livewire\Livewire;

// 在服务提供者的 boot() 方法中(例如 App\Providers\AppServiceProvider)
Livewire::addComponent(
    name: 'custom-button',
    viewPath: resource_path('views/ui/button.blade.php')
);

注册组件目录:

php
Livewire::addLocation(
    viewPath: resource_path('views/admin/components')
);

注册命名空间:

php
Livewire::addNamespace(
    namespace: 'ui',
    viewPath: resource_path('views/ui')
);

当你需要有条件地注册组件或构建提供 Livewire 组件的 Laravel 包时,这种方法很有用。

注册基于类的组件

对于基于类的组件,使用相同的方法,但使用 class 参数而不是 path

php
use Livewire\Livewire;

// 在服务提供者的 boot() 方法中(例如 App\Providers\AppServiceProvider)

// 注册单个基于类的组件
Livewire::addComponent(
    name: 'todos',
    class: \App\Livewire\Todos::class
);

// 注册基于类的组件位置
Livewire::addLocation(
    classNamespace: 'App\\Admin\\Livewire'
);

// 为基于类的组件创建命名空间
Livewire::addNamespace(
    namespace: 'admin',
    classNamespace: 'App\\Admin\\Livewire',
    classPath: app_path('Admin/Livewire'),
    classViewPath: resource_path('views/admin/livewire')
);

基于类的组件

对于从 Livewire v3 迁移的团队或喜欢更传统 Laravel 结构的团队,Livewire 完全支持基于类的组件。这种方法将 PHP 类和 Blade 视图分离到它们常规位置的不同文件中。

创建基于类的组件

shell
php artisan make:livewire CreatePost --class

这会创建两个单独的文件:

app/Livewire/CreatePost.php

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>

何时使用基于类的组件

在以下情况使用基于类的组件:

  • 从 Livewire v2/v3 迁移
  • 你的团队喜欢更传统的文件结构
  • 你有围绕基于类架构的既定约定

在以下情况使用单文件或多文件组件:

  • 开始新的 Livewire v4 项目
  • 你想要更好的组件集中化
  • 你想使用最新的 Livewire 约定

配置默认组件类型

如果你想默认使用基于类的组件,在 config/livewire.php 中配置:

php
'make_command' => [
    'type' => 'class',
],

自定义组件模板

你可以通过运行以下命令自定义 Livewire 用于生成新组件的文件(或称为 stubs):

shell
php artisan livewire:stubs

这会在你的应用程序中创建你可以修改的 stub 文件:

单文件组件 stubs:

  • stubs/livewire-sfc.stub — 单文件组件

多文件组件 stubs:

  • stubs/livewire-mfc-class.stub — 多文件组件的 PHP 类
  • stubs/livewire-mfc-view.stub — 多文件组件的 Blade 视图
  • stubs/livewire-mfc-js.stub — 多文件组件的 JavaScript
  • stubs/livewire-mfc-test.stub — 多文件组件的 Pest 测试

基于类的组件 stubs:

  • stubs/livewire.stub — 基于类组件的 PHP 类
  • stubs/livewire.view.stub — 基于类组件的 Blade 视图

额外 stubs:

  • stubs/livewire.attribute.stub — 属性类
  • stubs/livewire.form.stub — 表单类

发布后,Livewire 在生成新组件时将自动使用你的自定义 stubs。

故障排除

组件未找到

症状: 错误消息如"Component [post.create] not found"或"Unable to find component"

解决方案:

  • 验证组件文件位于预期路径
  • 检查视图中的组件名称是否与文件结构匹配(子目录使用点)
  • 对于命名空间组件,确保命名空间在 config/livewire.php 中定义或在服务提供者中手动注册
  • 尝试清除视图缓存:php artisan view:clear

组件显示空白或不渲染

常见原因:

  • Blade 模板中缺少根元素(Livewire 要求恰好一个根元素)
  • 组件 PHP 部分存在语法错误
  • 检查 Laravel 日志获取详细错误消息

类名冲突

症状: 使用单文件组件时出现关于重复类名的错误

解决方案: 如果你在不同目录中有多个同名的单文件组件,可能会发生这种情况。解决方案:

  • 重命名其中一个组件使其唯一
  • 为其中一个目录添加命名空间以获得更清晰的分离

另请参阅

  • 属性 — 管理组件状态和数据
  • 操作 — 使用方法处理用户交互
  • 页面 — 将组件用作带路由的完整页面
  • 嵌套 — 组合组件并在它们之间传递数据
  • 生命周期钩子 — 在组件生命周期的特定点执行代码