升级指南
自动升级工具
为了节省升级时间,我们包含了一个 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 软件包目前都支持 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 将加载其内部版本
通过脚本标签包含插件
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>
现在,您可以将 Alpine
和 Livewire
导入到应用程序的捆绑中,如下所示
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 的三个主要更改是
-
emit()
已重命名为dispatch()
(同样,emitTo()
和emitSelf()
现在是dispatchTo()
和dispatchSelf()
) -
dispatchBrowserEvent()
已重命名为dispatch()
- 所有事件参数都必须命名
有关更多信息,请查看新的事件文档页面。
以下是应应用于您的应用程序的“查找并替换”差异
-$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;
但是,建议你使用提供的 getPage
和 setPage
方法来修改和访问当前页面
// 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 的更新端点。