wire:stream

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

Livewire 允许您通过 wire:stream API 在请求完成之前将内容流式传输到网页。对于诸如 AI 聊天机器人(在生成响应时流式传输响应)之类的功能,这是一个非常有用的特性。

与 Laravel Octane 不兼容

Livewire 目前不支持将 wire:stream 与 Laravel Octane 一起使用。

为了演示 wire:stream 的最基本功能,下面是一个简单的 CountDown 组件,当按下按钮时,它会向用户显示从“3”到“0”的倒计时

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(
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>
</div>
HTML;
}
}

以下是用户在按下“开始倒计时”时从其角度发生的情况

  • 页面上显示“计数:3”
  • 他们按下“开始倒计时”按钮
  • 经过一秒钟,“计数:2”显示出来
  • 此过程持续到显示“计数: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 界面示例

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);
});
}
 
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>
</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 请求完成,用户收到完整响应。

替换与追加

使用 $this->stream() 将内容流式传输到元素时,你可以告诉 Livewire 用流式传输的内容替换目标元素的内容或将其追加到现有内容中。

根据场景的不同,替换或追加都是可取的。例如,在从聊天机器人流式传输响应时,通常需要追加(因此是默认设置)。但是,在显示倒计时等内容时,替换更合适。

您可以通过将 replace: 参数传递给 $this->stream(带有布尔值)进行配置

// Append contents...
$this->stream(to: 'target', content: '...');
 
// Replace contents...
$this->stream(to: 'target', content: '...', replace: true);

还可以通过追加或删除 .replace 修饰符在目标元素级别指定追加/替换

// Append contents...
<div wire:stream="target">
 
// Replace contents...
<div wire:stream.replace="target">