EventSource AKA Server-Sent Events (EventSource o mejor conocido como Server-Sent Events) + VUE JS + LARAVEL

Ok voy a escribir una nota breve sobre el server-sent event API , el cual está contenido en la interfaz EventSource; su función es abrir una conexión al servidor para recibir eventos de él. Se crea un nuevo objeto EventSource, especificando el URI de un script que genera los eventos, ejemplo gráfico y rápido a continuación:

El código

Primeramente el controlador ejemplo desde Laravel,

use Symfony\Component\HttpFoundation\StreamedResponse;
 
 
public function micontroladordivertido(){
 
$start = time();
$maxExecution = ini_get('max_execution_time');
$response = new StreamedResponse(function() use ($start, $maxExecution) {
while(true) {
        if(time() >= $start + $maxExecution) {
          exit();
        }
        echo 'data: ' . json_encode(Compras::all()) . "\n\n";
        ob_flush();
        flush();
        usleep(20000000);
    }
});

$response->headers->set('Content-Type', 'text/event-stream');
$response->headers->set('X-Accel-Buffering', 'no');
$response->headers->set('Cach-Control', 'no-cache');
return $response;
 


Ok, del lado del servidor sería eso únicamente, en el controlador encontramos $start y $maxExecution son variables para controlar el tiempo de respuesta, la clase $StreamedResponse nos ayuda a obtener datos del servidor sin necesidad de recargar. Dentro de $StreamedResponse añadimos un while que controla el tiempo de respuesta, si se supera entonces cerramos la conexión.
Añadimos headers al response donde le decimos que el Content-Type sea text/event-stream, esto permite que el navegador sepa que hacer con él, las otras líneas son para el caché y eso es todo.

Ahora del lado del cliente, que es donde viene lo interesante.

Haciendo uso de Vue JS

<script>
  new Vue({
  el: "#app",
  data: {
    items: null
  },
  created() {
    this.setupStream();
  },
  methods: {
    setupStream() {
      let es = new EventSource('/micontroladordivertido');
      es.addEventListener('message', event => {
          console.log(event);
          let data = JSON.parse(event.data);
          this.items = data.items;
      }, false);
      es.addEventListener('error', event => {
          if (event.readyState == EventSource.CLOSED) {
              console.log('Event was closed');
              console.log(EventSource);
          }
      }, false);
    }

  },
  beforeDestroy() {

  },
  destroy(){


  }

});


</script>
 

Explicando el código. Primeramente se crea el objeto Vue, añadimos el state created() donde llamamos a nuestro método setupStream() y dentro del array de methods incluimos el método setupStream(). Dentro de nuestro bonito método agregamos una constante donde alojamos la URL y dos addEventListener uno para success y otro para cachar errores, entonces en el primer listener cachamos el evento y luego lo añadimos a nuestra variable items de Vue  para posteriormente hacer loop y mostrarlo en pantalla, de otro modo se muestra un error.


Comentarios adicionales

En particular no llevé la solución a producción por distintas razones pero principalmente por el bajo rendimiento, pero para experimento está muy bien. Yo recomendaría usar Socket.io , Node.js y Vue.js con esto lograremos optimizar por mucho el streaming que queremos lograr, quizá más adelante realice un post con estas herramientas.

Happy coding!




Entradas populares de este blog

"php" no se reconoce como un comando interno o externo, programa o archivo por lotes ejecutable.

Laravel y MercadoPago SDK

Dompdf image not found or type unknown