主题
wire:stream
Livewire 允许你通过 wire:stream API 在请求完成之前将内容流式传输到网页。这对于在生成响应时流式传输响应的 AI 聊天机器人等事物是一个非常有用的功能。
与 Laravel Octane 不兼容
Livewire 目前不支持将 wire:stream 与 Laravel Octane 一起使用。
为了演示 wire:stream 的最基本功能,以下是一个简单的 CountDown 组件,当按下按钮时,向用户显示从"3"到"0"的倒计时:
php
use Livewire\Component;
class CountDown extends Component
{
public $start = 3;
public function begin()
{
while ($this->start >= 0) {
// Stream the current count to the browser...
$this->stream( // [tl! highlight:4]
to: 'count',
content: $this->start,
replace: true,
);
// Pause for 1 second between numbers...
sleep(1);
// Decrement the counter...
$this->start = $this->start - 1;
};
}
public function render()
{
return <<<'HTML'
<div>
<button wire:click="begin">Start count-down</button>
<h1>Count: <span wire:stream="count">{{ $start }}</span></h1> <!-- [tl! highlight] -->
</div>
HTML;
}
}当用户按下"开始倒计时"时,从用户的角度来看发生了什么:
- 页面上显示"Count: 3"
- 他们按下"开始倒计时"按钮
- 经过一秒钟,显示"Count: 2"
- 此过程继续,直到显示"Count: 0"
以上所有这些都发生在向服务器发出的单个网络请求期间。
当按下按钮时,从系统的角度来看发生了什么:
- 向 Livewire 发送请求以调用
begin()方法 - 调用
begin()方法并开始while循环 - 调用
$this->stream()并立即向浏览器启动"流式响应" - 浏览器接收流式响应,其中包含在组件中查找带有
wire:stream="count"的元素的指令,并用接收到的有效负载替换其内容(在第一个流式传输数字的情况下为"3") sleep(1)方法使服务器挂起一秒钟while循环重复,每秒流式传输新数字的过程继续,直到while条件为假- 当
begin()完成运行并且所有计数都已流式传输到浏览器时,Livewire 完成其请求生命周期,渲染组件并将最终响应发送到浏览器
流式传输聊天机器人响应
wire:stream 的一个常见用例是在从支持流式响应的 API(如 OpenAI 的 ChatGPT)接收聊天机器人响应时流式传输它们。
以下是使用 wire:stream 实现类似 ChatGPT 界面的示例:
php
use Livewire\Component;
class ChatBot extends Component
{
public $prompt = '';
public $question = '';
public $answer = '';
function submitPrompt()
{
$this->question = $this->prompt;
$this->prompt = '';
$this->js('$wire.ask()');
}
function ask()
{
$this->answer = OpenAI::ask($this->question, function ($partial) {
$this->stream(to: 'answer', content: $partial); // [tl! highlight]
});
}
public function render()
{
return <<<'HTML'
<div>
<section>
<div>ChatBot</div>
@if ($question)
<article>
<hgroup>
<h3>User</h3>
<p>{{ $question }}</p>
</hgroup>
<hgroup>
<h3>ChatBot</h3>
<p wire:stream="answer">{{ $answer }}</p> <!-- [tl! highlight] -->
</hgroup>
</article>
@endif
</section>
<form wire:submit="submitPrompt">
<input wire:model="prompt" type="text" placeholder="Send a message" autofocus>
</form>
</div>
HTML;
}
}以下是上述示例中发生的情况:
- 用户在标记为"发送消息"的文本字段中键入以向聊天机器人提问。
- 他们按下 [Enter] 键。
- 向服务器发送网络请求,将消息设置为
$question属性,并清除$prompt属性。 - 响应被发送回浏览器,输入被清除。由于调用了
$this->js('...'),因此触发了对服务器的新请求,调用ask()方法。 ask()方法调用 ChatBot API 并通过回调中的$partial参数接收流式响应部分。- 每个
$partial都被流式传输到浏览器到页面上的wire:stream="answer"元素中,向用户逐步显示答案。 - 当接收到整个响应时,Livewire 请求完成,用户收到完整响应。
替换 vs. 追加
使用 $this->stream() 将内容流式传输到元素时,你可以告诉 Livewire 用流式传输的内容替换目标元素的内容或将它们追加到现有内容。
根据场景,替换或追加都可能是可取的。例如,在从聊天机器人流式传输响应时,通常需要追加(因此是默认值)。但是,在显示倒计时之类的内容时,替换更合适。
你可以通过向 $this->stream 传递带有布尔值的 replace: 参数来配置任一选项:
php
// 追加内容...
$this->stream(to: 'target', content: '...');
// 替换内容...
$this->stream(to: 'target', content: '...', replace: true);还可以通过追加或删除 .replace 修饰符在目标元素级别指定追加/替换:
blade
// 追加内容...
<div wire:stream="target">
// 替换内容...
<div wire:stream.replace="target">