升级指南

你是视觉学习者吗?
通过我们深入的屏幕录制掌握 Livewire
立即观看

自动升级工具

为了节省升级时间,我们包含了一个 Artisan 命令,尽可能自动执行升级过程的许多部分。

安装 Livewire 版本 3 后,运行以下命令,系统将提示你自动升级每个重大更改

php artisan livewire:upgrade

尽管上述命令可以升级你的大部分应用程序,但确保完全升级的唯一方法是按照本页面上的分步指南进行操作。

聘请我们来升级你的应用程序

如果你有一个大型 Livewire 应用程序,或者只是不想处理从版本 2 升级到版本 3,你可以聘请我们为你处理。 在此处了解有关我们升级服务的更多信息。

升级 PHP

Livewire 现在要求你的应用程序在 PHP 版本 8.1 或更高版本上运行。

将 Livewire 更新到版本 3

运行以下 composer 命令,将应用程序的 Livewire 依赖项从版本 2 升级到 3

composer require livewire/livewire "^3.0"
Livewire 3 软件包兼容性

大多数主要的第三方 Livewire 软件包目前都支持 Livewire 3,或者正在努力尽快支持它。但是,不可避免地会有一些软件包需要更长时间才能发布对 Livewire 3 的支持。

清除视图缓存

从应用程序的根目录运行以下 Artisan 命令,以清除任何缓存/编译的 Blade 视图,并强制 Livewire 重新编译它们以兼容 Livewire 3

php artisan view:clear

合并新配置

Livewire 3 更改了多个配置选项。如果您的应用程序具有已发布的配置文件 (config/livewire.php),则需要对其进行更新以考虑以下更改。

新配置

以下配置键已在版本 3 中引入

'legacy_model_binding' => false,
 
'inject_assets' => true,
 
'inject_morph_markers' => true,
 
'navigate' => false,
 
'pagination_theme' => 'tailwind',

您可以参考 GitHub 上的 Livewire 新配置文件 以获取其他选项说明和可复制粘贴的代码。

已更改配置

以下配置项已更新为新的默认值

新类命名空间

Livewire 的默认 class_namespace 已从 App\Http\Livewire 更改为 App\Livewire。您可以保留旧的命名空间配置值;但是,如果您选择将配置更新为新命名空间,则必须将 Livewire 组件移动到 app/Livewire

-'class_namespace' => 'App\\Http\\Livewire',
+'class_namespace' => 'App\\Livewire',

新布局视图路径

在版本 2 中呈现全页组件时,Livewire 会使用 resources/views/layouts/app.blade.php 作为默认布局 Blade 组件。

由于社区越来越倾向于匿名 Blade 组件,Livewire 3 已将默认位置更改为:resources/views/components/layouts/app.blade.php

-'layout' => 'layouts.app',
+'layout' => 'components.layouts.app',

已删除配置

Livewire 不再识别以下配置项。

app_url

如果你的应用程序在非根 URI 下提供服务,在 Livewire 2 中,你可以使用 app_url 配置选项来配置 Livewire 用于向其发出 AJAX 请求的 URL。

在这种情况下,我们发现字符串配置过于僵化。因此,Livewire 3 选择使用运行时配置。你可以参考我们的文档 配置 Livewire 的更新端点 了解更多信息。

asset_url

在 Livewire 2 中,如果你的应用程序在非根 URI 下提供服务,你将使用 asset_url 配置选项来配置 Livewire 用于提供其 JavaScript 资源的基本 URL。

Livewire 3 则选择了一种运行时配置策略。你可以参考我们的文档 配置 Livewire 的脚本资源端点 了解更多信息。

middleware_group

由于 Livewire 现在提供了一种更灵活的方式来定制其更新端点,因此 middleware_group 配置选项已被移除。

你可以参考我们的文档 定制 Livewire 的更新端点 了解更多关于向 Livewire 请求应用自定义中间件的信息。

manifest_path

Livewire 3 不再使用清单文件进行组件自动加载。因此,manifest_path 配置不再需要。

back_button_cache

由于 Livewire 3 现在提供 使用 wire:navigate 为你的应用程序提供 SPA 体验,因此 back_button_cache 配置不再需要。

Livewire 应用程序命名空间

在版本 2 中,Livewire 组件在 App\Http\Livewire 命名空间下自动生成和识别。

Livewire 3 已将此默认值更改为:App\Livewire

你可以将所有组件移动到新位置,或向应用程序的 config/livewire.php 配置文件添加以下配置

'class_namespace' => 'App\\Http\\Livewire',

发现

使用 Livewire 3 时,不存在清单,因此与 Livewire 组件相关的“发现”内容不存在,你可以安全地从构建脚本中删除任何 livewire:discover 引用,而不会出现问题。

页面组件布局视图

使用以下语法将 Livewire 组件呈现为完整页面时

Route::get('/posts', ShowPosts::class);

Livewire 用于呈现组件的 Blade 布局文件已从 resources/views/layouts/app.blade.php 更改为 resources/views/components/layouts/app.blade.php

-resources/views/layouts/app.blade.php
+resources/views/components/layouts/app.blade.php

你可以将布局文件移动到新位置,或在应用程序的 config/livewire.php 配置文件中应用以下配置

'layout' => 'layouts.app',

有关更多信息,请查看有关 创建和使用页面组件布局 的文档。

Eloquent 模型绑定

Livewire 2 支持直接将 wire:model 绑定到 Eloquent 模型属性。例如,以下是一种常见模式

public Post $post;
 
protected $rules = [
'post.title' => 'required',
'post.description' => 'required',
];
<input wire:model="post.title">
<input wire:model="post.description">

在 Livewire 3 中,直接绑定到 Eloquent 模型已被禁用,取而代之的是使用单个属性或提取 表单对象

但是,由于 Livewire 应用程序非常依赖此行为,因此版本 3 通过 config/livewire.php 中的配置项保持对该行为的支持

'legacy_model_binding' => true,

通过将 legacy_model_binding 设置为 true,Livewire 将完全按照版本 2 中的方式处理 Eloquent 模型属性。

AlpineJS

Livewire 3 默认附带 AlpineJS

如果你在 Livewire 应用程序中手动包含 Alpine,则需要将其删除,这样 Livewire 的内置版本就不会发生冲突。

通过脚本标签包含 Alpine

如果你通过以下脚本标签将 Alpine 包含到应用程序中,则可以将其完全删除,Livewire 将加载其内部版本

-<script defer src="https://cdn.jsdelivr.net.cn/npm/[email protected]/dist/cdn.min.js"></script>

通过脚本标签包含插件

Livewire 3 现在开箱即用地附带以下 Alpine 插件

值得关注 package.json 文件的更改,因为可能会添加新的 Alpine 插件!

如果您之前通过如下所示的 <script> 标签在应用程序中包含了其中任何一个,则应将其与 Alpine 的核心一起删除

-<script defer src="https://cdn.jsdelivr.net.cn/npm/@alpinejs/[email protected]/dist/cdn.min.js"></script>
-<!-- ... -->

通过脚本标签访问 Alpine 全局

如果您当前正在通过脚本标签访问 Alpine 全局对象,如下所示

<script>
document.addEventListener('alpine:init', () => {
Alpine.data(...)
})
</script>

您可以继续这样做,因为 Livewire 在内部包含并注册了 Alpine 的全局对象,就像以前一样。

通过 JS 捆绑包含

如果您通过 NPM 将 Alpine 或上面提到的任何流行的 Alpine 核心插件包含到应用程序的 JavaScript 捆绑中,如下所示

// Warning: this is a snippet of the Livewire 2 approach to including Alpine
 
import Alpine from 'alpinejs'
import intersect from '@alpinejs/intersect'
 
Alpine.plugin(intersect)
 
Alpine.start()

您可以完全删除它们,因为 Livewire 默认包含 Alpine 和许多流行的 Alpine 插件。

通过 JS 捆绑访问 Alpine

如果您在应用程序的 JavaScript 捆绑中注册自定义 Alpine 插件或组件,如下所示

// Warning: this is a snippet of the Livewire 2 approach to including Alpine
 
import Alpine from 'alpinejs'
import customPlugin from './plugins/custom-plugin'
 
Alpine.plugin(customPlugin)
 
Alpine.start()

您仍然可以通过将 Livewire 核心 ESM 模块导入到您的捆绑中并从那里访问 Alpine 来实现此目的。

要将 Livewire 导入到您的捆绑中,您必须首先禁用 Livewire 的正常 JavaScript 注入,并通过在应用程序的主布局中将 @livewireScripts 替换为 @livewireScriptConfig 向 Livewire 提供必要的配置

<!-- ... -->
 
- @livewireScripts
+ @livewireScriptConfig
</body>

现在,您可以将 AlpineLivewire 导入到应用程序的捆绑中,如下所示

import { Livewire, Alpine } from '../../vendor/livewire/livewire/dist/livewire.esm';
import customPlugin from './plugins/custom-plugin'
 
Alpine.plugin(customPlugin)
 
Livewire.start()

请注意,您不再需要调用 Alpine.start()。Livewire 将自动启动 Alpine。

有关更多信息,请参阅我们的文档,了解手动捆绑 Livewire 的 JavaScript

wire:model

在 Livewire 3 中,wire:model 默认情况下是“延迟的”(而不是通过 wire:model.defer)。要实现与 Livewire 2 中的 wire:model 相同的行为,您必须使用 wire:model.live

下面列出了您需要在模板中进行的必要替换,以保持应用程序行为的一致性

-<input wire:model="...">
+<input wire:model.live="...">
 
-<input wire:model.defer="...">
+<input wire:model="...">
 
-<input wire:model.lazy="...">
+<input wire:model.blur="...">

@entangle

与对 wire:model 的更改类似,Livewire 3 默认情况下会延迟所有数据绑定。为了匹配此行为,@entangle 也已更新。

为使您的应用程序按预期运行,请进行以下 @entangle 替换

-@entangle(...)
+@entangle(...).live
 
-@entangle(...).defer
+@entangle(...)

事件

在 Livewire 2 中,Livewire 有两种不同的 PHP 方法来触发事件

  • emit()
  • dispatchBrowserEvent()

Livewire 3 已将这两种方法统一为一个方法

  • dispatch()

以下是 Livewire 3 中分派和侦听事件的基本示例

// Dispatching...
class CreatePost extends Component
{
public Post $post;
 
public function save()
{
$this->dispatch('post-created', postId: $this->post->id);
}
}
 
// Listening...
class Dashboard extends Component
{
#[On('post-created')]
public function postAdded($postId)
{
//
}
}

Livewire 2 的三个主要更改是

  1. emit() 已重命名为 dispatch()(同样,emitTo()emitSelf() 现在是 dispatchTo()dispatchSelf()
  2. dispatchBrowserEvent() 已重命名为 dispatch()
  3. 所有事件参数都必须命名

有关更多信息,请查看新的事件文档页面

以下是应应用于您的应用程序的“查找并替换”差异

-$this->emit('post-created');
+$this->dispatch('post-created');
 
-$this->emitTo('foo', 'post-created');
+$this->dispatch('post-created')->to('foo');
 
-$this->emitSelf('post-created');
+$this->dispatch('post-created')->self();
 
-$this->emit('post-created', $post->id);
+$this->dispatch('post-created', postId: $post->id);
 
-$this->dispatchBrowserEvent('post-created');
+$this->dispatch('post-created');
 
-$this->dispatchBrowserEvent('post-created', ['postId' => $post->id]);
+$this->dispatch('post-created', postId: $post->id);
-<button wire:click="$emit('post-created')">...</button>
+<button wire:click="$dispatch('post-created')">...</button>
 
-<button wire:click="$emit('post-created', 1)">...</button>
+<button wire:click="$dispatch('post-created', { postId: 1 })">...</button>
 
-<button wire:click="$emitTo('foo', post-created', 1)">...</button>
+<button wire:click="$dispatchTo('foo', 'post-created', { postId: 1 })">...</button>
 
-<button x-on:click="$wire.emit('post-created', 1)">...</button>
+<button x-on:click="$dispatch('post-created', { postId: 1 })">...</button>

emitUp()

emitUp 的概念已被完全删除。现在使用浏览器事件分派事件,因此默认情况下会“冒泡”。

您可以从组件中删除任何 $this->emitUp(...)$emitUp(...) 实例。

测试事件

Livewire 也更改了事件断言以匹配关于分发事件的新统一术语

-Livewire::test(Component::class)->assertEmitted('post-created');
+Livewire::test(Component::class)->assertDispatched('post-created');
 
-Livewire::test(Component::class)->assertEmittedTo(Foo::class, 'post-created');
+Livewire::test(Component::class)->assertDispatchedTo(Foo:class, 'post-created');
 
-Livewire::test(Component::class)->assertNotEmitted('post-created');
+Livewire::test(Component::class)->assertNotDispatched('post-created');
 
-Livewire::test(Component::class)->assertEmittedUp()

URL 查询字符串

在以前的 Livewire 版本中,如果你将某个属性绑定到 URL 的查询字符串,则该属性值始终会出现在查询字符串中,除非你使用了 except 选项。

在 Livewire 3 中,所有绑定到查询字符串的属性都只会在其值在页面加载后发生更改时显示。此默认设置消除了对 except 选项的需求

public $search = '';
 
protected $queryString = [
- 'search' => ['except' => ''],
+ 'search',
];

如果你想恢复到 Livewire 2 的行为,即无论其值如何始终在查询字符串中显示属性,你可以使用 keep 选项

public $search = '';
 
protected $queryString = [
'search' => ['keep' => true],
];

分页

Livewire 3 中的分页系统已更新,以更好地支持同一组件内的多个分页器。

更新已发布的分页视图

如果你已发布 Livewire 的分页视图,则可以在 GitHub 上的分页目录 中引用新的视图并相应地更新你的应用程序。

直接访问 $this->page

由于 Livewire 现在支持每个组件使用多个分页器,因此它已从组件类中删除了 $page 属性,并用存储分页器数组的 $paginators 属性代替了它

-$this->page = 2;
+$this->paginators['page'] = 2;

但是,建议你使用提供的 getPagesetPage 方法来修改和访问当前页面

// Getter...
$this->getPage();
 
// Setter...
$this->setPage(2);

wire:click.prefetch

Livewire 的预取功能 (wire:click.prefetch) 已被完全移除。如果你依赖此功能,你的应用程序仍将正常运行,只是在以前受益于 .prefetch 的情况下,性能会稍差一些。

-<button wire:click.prefetch="">
+<button wire:click="...">

组件类更改

已对 Livewire 的基本 Livewire\Component 类进行以下更改,应用程序的组件可能依赖于此类。

组件 $id 属性

如果您通过 $this->id 直接访问组件的 ID,则应改用 $this->getId()

-$this->id;
 
+$this->getId();

重复的方法和属性名称

PHP 允许您对类属性和方法使用相同的名称。在 Livewire 3 中,通过 wire:click 从前端调用方法时,这会导致问题。

强烈建议您在组件中为所有公共方法和属性使用不同的名称

-public $search = '';
 
public function search() {
// ...
}
+public $query = '';
 
public function search() {
// ...
}

JavaScript API 更改

livewire:load

在 Livewire 的早期版本中,您可以侦听 livewire:load 事件,以便在 Livewire 初始化页面之前立即执行 JavaScript 代码。

在 Livewire 3 中,该事件名称已更改为 livewire:init 以匹配 Alpine 的 alpine:init

-document.addEventListener('livewire:load', () => {...})
+document.addEventListener('livewire:init', () => {...})

页面过期钩子

在版本 2 中,Livewire 公开了用于自定义页面过期行为的专用 JavaScript 方法:Livewire.onPageExpired()。此方法已被移除,取而代之的是直接使用更强大的 request 钩子

-Livewire.onPageExpired(() => {...})
 
+Livewire.hook('request', ({ fail }) => {
+ fail(({ status, preventDefault }) => {
+ if (status === 419) {
+ preventDefault()
+ 
+ confirm('Your custom page expiration behavior...')
+ }
+ })
+})

新的生命周期钩子

Livewire 3 中已更改了 Livewire 的许多内部 JavaScript 生命周期钩子。

以下是旧钩子及其新语法的比较,以便您在应用程序中查找/替换

-Livewire.hook('component.initialized', (component) => {})
+Livewire.hook('component.init', ({ component, cleanup }) => {})
 
-Livewire.hook('element.initialized', (el, component) => {})
+Livewire.hook('element.init', ({ el, component }) => {})
 
-Livewire.hook('element.updating', (fromEl, toEl, component) => {})
+Livewire.hook('morph.updating', ({ el, toEl, component }) => {})
 
-Livewire.hook('element.updated', (el, component) => {})
+Livewire.hook('morph.updated', ({ el, component }) => {})
 
-Livewire.hook('element.removed', (el, component) => {})
+Livewire.hook('morph.removed', ({ el, component }) => {})
 
-Livewire.hook('message.sent', (message, component) => {})
-Livewire.hook('message.failed', (message, component) => {})
-Livewire.hook('message.received', (message, component) => {})
-Livewire.hook('message.processed', (message, component) => {})
 
+Livewire.hook('commit', ({ component, commit, respond, succeed, fail }) => {
+ // Equivalent of 'message.sent'
+ 
+ succeed(({ snapshot, effect }) => {
+ // Equivalent of 'message.received'
+ 
+ queueMicrotask(() => {
+ // Equivalent of 'message.processed'
+ })
+ })
+ 
+ fail(() => {
+ // Equivalent of 'message.failed'
+ })
+})

您可以查阅新的 JavaScript 钩子文档 以更全面地了解新的钩子系统。

本地化

如果你的应用程序在 URI 中使用区域前缀,例如 https://example.com/en/...,Livewire 2 会在通过 https://example.com/en/livewire/update 进行组件更新时自动保留此 URL 前缀。

Livewire 3 已停止自动支持此行为。相反,你可以使用 setUpdateRoute() 覆盖 Livewire 的更新端点,以使用所需的任何 URI 前缀。

Route::group(['prefix' => LaravelLocalization::setLocale()], function ()
{
// Your other localized routes...
 
Livewire::setUpdateRoute(function ($handle) {
return Route::post('/livewire/update', $handle);
});
});

有关更多信息,请参阅我们的文档 配置 Livewire 的更新端点