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>

为什么使用 ⚡ emoji?

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

emoji 是完全可选的,如果你觉得不习惯,可以在 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/
├── post.create.php        # PHP 类
├── post.create.blade.php  # Blade 模板
├── post.create.js         # JavaScript (可选,使用 --js 标志)
└── post.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 关注点分离
  • 更适合有既定惯例的团队
  • 参见下面的基于类的组件

渲染组件

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

blade
<livewire:post.create />

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

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

blade
<livewire:post.create />

对于页面组件,使用命名空间前缀:

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; // Automatically set from prop

    // ...
};

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

$title 属性不会在初始页面加载后外部 :title="$initialValue" 改变时自动更新。这是使用 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; // Automatically bound from route

    // No mount() needed - Livewire handles it automatically
};

页面组件

组件可以使用 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>{{ $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_paths' => [
    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',
    path: resource_path('views/ui/button.blade.php')
);

注册组件目录:

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

注册命名空间:

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

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

基于类的组件

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

创建基于类的组件

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 迁移
  • 你的团队更喜欢传统的 Laravel 文件结构
  • 你已经建立了围绕基于类的架构的惯例
  • 在重视一致分离模式的大型团队中工作

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

  • 启动新的 Livewire v4 项目
  • 你想要更好的组件归置
  • 你的团队更喜欢现代的基于组件的模式
  • 你想要单文件简单性的好处,并可以选择以后拆分

配置默认组件类型

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

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

注册基于类的组件

基于类的组件可以使用之前显示的相同方法手动注册,但使用 class 参数而不是 path:

php
use Livewire\Livewire;
use App\Livewire\CustomButton;

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

// 注册单个基于类的组件
Livewire::addComponent(
    name: 'custom-button',
    class: CustomButton::class
);

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

// 为基于类的组件创建命名空间
Livewire::addNamespace(
    namespace: 'admin',
    class: 'App\\Admin\\Livewire'
);

自定义组件存根

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

shell
php artisan livewire:stubs

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

单文件组件存根:

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

多文件组件存根:

  • 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/livewire.stub — 基于类的组件的 PHP 类
  • stubs/livewire.view.stub — 基于类的组件的 Blade 视图

额外的存根:

  • stubs/livewire.attribute.stub — 属性类
  • stubs/livewire.form.stub — 表单类
  • stubs/livewire.test.stub — PHPUnit 测试文件
  • stubs/livewire.pest-test.stub — Pest 测试文件

一旦发布,Livewire 将在生成新组件时自动使用你的自定义存根。

故障排除

找不到组件

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

解决方案:

  • 验证组件文件存在于预期路径
  • 检查视图中的组件名称是否与文件结构匹配(子目录使用点)
  • 对于命名空间组件,确保命名空间在 config/livewire.php 中定义
  • 尝试清除视图缓存:php artisan view:clear
  • 如果使用基于类的组件,确保 PHP 文件中的命名空间正确

组件显示空白或不渲染

常见原因:

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

命名空间不工作

症状: 找不到像 pages::post.create 这样的命名空间组件

解决方案:

  • 确保命名空间在 config/livewire.phpcomponent_namespaces 下定义
  • 检查路径映射是否正确:'pages' => resource_path('views/pages')
  • 验证组件文件存在于正确的目录中
  • 清除配置缓存:php artisan config:clear

类名冲突

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

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

  • 将其中一个组件重命名为唯一的
  • 转换为基于类的组件,你可以完全控制命名空间

下一步

现在你已经了解了 Livewire 组件,以下是接下来要探索的关键概念:

  • 属性 — 了解组件属性如何工作,包括类型、安全性和响应性
  • 操作 — 了解如何使用方法和事件处理用户交互
  • 表单 — 使用实时验证和文件上传构建强大的表单
  • 页面 — 掌握页面组件、布局和路由
  • 嵌套 — 学习如何组合组件并在它们之间通信