Alpine
AlpineJS 是一个轻量级 JavaScript 库,可轻松为您的网页添加客户端交互性。它最初是为了补充 Livewire 等工具而构建的,其中更以 JavaScript 为中心的实用程序有助于在您的应用中散布交互性。
Livewire 开箱即用 Alpine,因此无需单独将其安装到您的项目中。
了解如何使用 AlpineJS 的最佳场所是 Alpine 文档。
基本 Alpine 组件
为了为本文档的其余部分奠定基础,这里有一个最简单且最具参考性的 Alpine 组件示例。一个小的“计数器”,它在页面上显示一个数字,并允许用户通过单击按钮增加该数字
<!-- Declare a JavaScript object of data... --><div x-data="{ count: 0 }"> <!-- Render the current "count" value inside an element... --> <h2 x-text="count"></h2> <!-- Increment the "count" value by "1" when a click event is dispatched... --> <button x-on:click="count++">+</button></div>
上面的 Alpine 组件可以在您的应用程序中的任何 Livewire 组件中使用,而不会出现任何问题。Livewire 负责在 Livewire 组件更新期间维护 Alpine 的状态。从本质上讲,您应该可以随意在 Livewire 中使用 Alpine 组件,就像在任何其他非 Livewire 上下文中使用 Alpine 一样。
在 Livewire 中使用 Alpine
让我们探索一个更真实的示例,在 Livewire 组件中使用 Alpine 组件。
下面是一个简单的 Livewire 组件,显示来自数据库的帖子模型的详细信息。默认情况下,只显示帖子的标题
<div> <h1>{{ $post->title }}</h1> <div x-data="{ expanded: false }"> <button type="button" x-on:click="expanded = ! expanded"> <span x-show="! expanded">Show post content...</span> <span x-show="expanded">Hide post content...</span> </button> <div x-show="expanded"> {{ $post->content }} </div> </div></div>
通过使用 Alpine,我们可以在用户按下“显示帖子内容...”按钮之前隐藏帖子内容。此时,Alpine 的 expanded
属性将设置为 true
,并且内容将显示在页面上,因为 x-show="content"
用于让 Alpine 控制帖子内容的可见性。
这是一个 Alpine 闪耀的示例:在不触发 Livewire 服务器往返的情况下向应用程序中添加交互性。
$wire
从 Alpine 控制 Livewire
使用 作为 Livewire 开发人员,您可以使用最强大的功能之一是 $wire
。$wire
对象是一个神奇的对象,可供所有在 Livewire 中使用的 Alpine 组件使用。
您可以将 $wire
视为从 JavaScript 到 PHP 的网关。它允许您访问和修改 Livewire 组件属性、调用 Livewire 组件方法,以及更多操作;所有这些都可以在 AlpineJS 中进行。
访问 Livewire 属性
以下是一个简单的“字符计数”实用程序示例,用于创建帖子的表单中。这将立即向用户显示他们在键入时帖子内容中包含多少个字符
<form wire:submit="save"> <!-- ... --> <input wire:model="content" type="text"> <small> Character count: <span x-text="$wire.content.length"></span> </small> <button type="submit">Save</button></form>
如您所见,上面的示例中正在使用 x-text
来允许 Alpine 控制 <span>
元素的文本内容。x-text
接受其内部的任何 JavaScript 表达式,并在任何依赖项更新时自动做出反应。由于我们使用 $wire.content
访问 $content
的值,因此每当 $wire.content
从 Livewire 更新时,Alpine 都会自动更新文本内容;在这种情况下,通过 wire:model="content"
更新。
更改 Livewire 属性
以下是在 Alpine 中使用 $wire
清除用于创建帖子的表单的“标题”字段的示例。
<form wire:submit="save"> <input wire:model="title" type="text"> <button type="button" x-on:click="$wire.title = ''">Clear</button> <!-- ... --> <button type="submit">Save</button></form>
当用户填写上面的 Livewire 表单时,他们可以按“清除”,标题字段将被清除,而无需从 Livewire 发送网络请求。交互将是“即时的”。
以下是实现此目的的简要说明
-
x-on:click
告诉 Alpine 监听按钮元素上的点击事件 - 点击时,Alpine 运行提供的 JS 表达式:
$wire.title = ''
- 由于
$wire
是表示 Livewire 组件的魔术对象,因此可以从 JavaScript 直接访问或更改组件中的所有属性 -
$wire.title = ''
将 Livewire 组件中$title
的值设置为一个空字符串 - 任何 Livewire 实用程序(如
wire:model
)都会立即对这一更改做出反应,而无需发送服务器往返请求 - 在下一个 Livewire 网络请求中,
$title
属性将在后端更新为空字符串
调用 Livewire 方法
Alpine 还可以通过直接在 $wire
上调用任何 Livewire 方法/操作来轻松调用它们。
以下是一个使用 Alpine 监听输入上的“模糊”事件并触发表单保存的示例。“模糊”事件是由浏览器在用户按“tab”键从当前元素移除焦点并聚焦于页面上的下一个元素时触发的
<form wire:submit="save"> <input wire:model="title" type="text" x-on:blur="$wire.save()"> <!-- ... --> <button type="submit">Save</button></form>
通常,在这种情况下,你只需使用 wire:model.blur="title"
,但是,了解如何使用 Alpine 实现此目的有助于演示。
传递参数
你还可以通过将参数传递给 $wire
方法调用来将参数传递给 Livewire 方法。
考虑一个具有 deletePost()
方法的组件,如下所示
public function deletePost($postId){ $post = Post::find($postId); // Authorize user can delete... auth()->user()->can('update', $post); $post->delete();}
现在,你可以像这样从 Alpine 将 $postId
参数传递给 deletePost()
方法
<button type="button" x-on:click="$wire.deletePost(1)">
通常,会在 Blade 中生成类似 $postId
的内容。以下是如何使用 Blade 确定 Alpine 传递给 deletePost()
的 $postId
的示例
@foreach ($posts as $post) <button type="button" x-on:click="$wire.deletePost({{ $post->id }})"> Delete "{{ $post->title }}" </button>@endforeach
如果页面上有三个帖子,则上述 Blade 模板将在浏览器中呈现为类似以下内容
<button type="button" x-on:click="$wire.deletePost(1)"> Delete "The power of walking"</button> <button type="button" x-on:click="$wire.deletePost(2)"> Delete "How to record a song"</button> <button type="button" x-on:click="$wire.deletePost(3)"> Delete "Teach what you learn"</button>
如你所见,我们已经使用 Blade 将不同的帖子 ID 呈现到 Alpine x-on:click
表达式中。
Blade 参数“陷阱”
这是一个非常强大的技术,但在阅读 Blade 模板时可能会让人感到困惑。乍一看很难知道哪些部分是 Blade,哪些部分是 Alpine。因此,检查页面上呈现的 HTML 以确保你预期呈现的内容准确无误会很有帮助。
以下是一个通常让人困惑的示例
假设你的 Post 模型不使用 ID,而是使用 UUID 作为索引(ID 是整数,UUID 是很长的字符串)。
如果我们像使用 ID 一样呈现以下内容,就会出现问题
<!-- Warning: this is an example of problematic code... --><button type="button" x-on:click="$wire.deletePost({{ $post->uuid }})">
上面的 Blade 模板将在你的 HTML 中呈现以下内容
<!-- Warning: this is an example of problematic code... --><button type="button" x-on:click="$wire.deletePost(93c7b04c-c9a4-4524-aa7d-39196011b81a)">
注意 UUID 字符串周围没有引号吗?当 Alpine 评估此表达式时,JavaScript 将抛出错误:“未捕获的语法错误:无效或意外的令牌”。
要解决此问题,我们需要像这样在 Blade 表达式周围添加引号
<button type="button" x-on:click="$wire.deletePost('{{ $post->uuid }}')">
现在,上面的模板将正确呈现,并且一切都会按预期工作
<button type="button" x-on:click="$wire.deletePost('93c7b04c-c9a4-4524-aa7d-39196011b81a')">
刷新组件
你可以使用 $wire.$refresh()
轻松刷新 Livewire 组件(触发网络往返以重新呈现组件的 Blade 视图)。
<button type="button" x-on:click="$wire.$refresh()">
$wire.entangle
共享状态
使用 在大多数情况下,$wire
是你从 Alpine 与 Livewire 状态交互所需的一切。但是,Livewire 提供了一个额外的 $wire.entangle()
实用程序,可用于使 Livewire 中的值与 Alpine 中的值保持同步。
为了演示,请考虑这个下拉示例,它的 showDropdown
属性通过 $wire.entangle()
在 Livewire 和 Alpine 之间纠缠。通过使用纠缠,我们现在可以从 Alpine 和 Livewire 控制下拉状态
use Livewire\Component; class PostDropdown extends Component{ public $showDropdown = false; public function archive() { // ... $this->showDropdown = false; } public function delete() { // ... $this->showDropdown = false; }}
<div x-data="{ open: $wire.entangle('showDropdown') }"> <button x-on:click="open = true">Show More...</button> <ul x-show="open" x-on:click.outside="open = false"> <li><button wire:click="archive">Archive</button></li> <li><button wire:click="delete">Delete</button></li> </ul></div>
用户现在可以使用 Alpine 立即切换下拉,但当他们单击 Livewire 操作(如“存档”)时,Livewire 将被告知关闭下拉。Alpine 和 Livewire 都可以操作各自的属性,而另一个属性将自动更新。
默认情况下,更新状态(客户端上的更改,但服务器上不会立即更新)将被延迟,直到下一次 Livewire 请求。如果你需要在用户点击时立即更新服务器端状态,请像这样链接 .live
修饰符
<div x-data="{ open: $wire.entangle('showDropdown').live }"> ...</div>
$wire.entangle
在大多数情况下,你可以通过使用 $wire
直接从 Alpine 访问 Livewire 属性,而不是对其进行纠缠,来实现你的目标。纠缠两个属性而不是依赖一个属性,在使用频繁更改的深度嵌套对象时可能会导致可预测性和性能问题。出于这个原因,从版本 3 开始,Livewire 的文档中不再强调 $wire.entangle
。
在 Livewire 版本 2 中,建议使用 Blade 的 @entangle
指令。在 v3 中不再是这样。首选 $wire.entangle()
,因为它是一个更健壮的实用程序,并且避免了某些 在移除 DOM 元素时出现的问题。
在你的 JavaScript 构建中手动捆绑 Alpine
默认情况下,Livewire 和 Alpine 的 JavaScript 会自动注入到每个 Livewire 页面中。
这对于简单的设置来说是理想的,但是,你可能希望将你自己的 Alpine 组件、存储和插件包含到你的项目中。
通过你自己的 JavaScript 捆绑在页面上包含 Livewire 和 Alpine 非常简单。
首先,你必须在你的布局文件中包含 @livewireScriptConfig
指令,如下所示
<html><head> <!-- ... --> @livewireStyles @vite(['resources/js/app.js'])</head><body> {{ $slot }} @livewireScriptConfig </body></html>
这允许 Livewire 为你的捆绑提供它为你的应用程序正常运行所需的某些配置。
现在,你可以像这样在你的 resources/js/app.js
文件中导入 Livewire 和 Alpine
import { Livewire, Alpine } from '../../vendor/livewire/livewire/dist/livewire.esm'; // Register any Alpine directives, components, or plugins here... Livewire.start()
以下是在你的应用程序中注册一个名为“x-clipboard”的自定义 Alpine 指令的示例
import { Livewire, Alpine } from '../../vendor/livewire/livewire/dist/livewire.esm'; Alpine.directive('clipboard', (el) => { let text = el.textContent el.addEventListener('click', () => { navigator.clipboard.writeText(text) })}) Livewire.start()
现在,x-clipboard
指令将可用于你的 Livewire 应用程序中的所有 Alpine 组件。