DEV Community

Joel Humberto Gómez Paredes
Joel Humberto Gómez Paredes

Posted on

Instant Navigation con Speculation Rules API

Uno de las fantasías de todo frontend es que la navegación entre las diferentes partes de nuestra web sea instantanea como si de alguna manera la web estuviera lista para la página a la que queremos acceder.

El browser nos ofrece mecanismos como los resource hints para poder hacer precargas (entre los mas conocidos preload y prefetch) de recursos lo cual nos da la posibilidad de reducir tiempos de carga.

Antes de tenía un resource hint llamado prerender pero fue eliminado por múltiples bugs pero ahora regreso en forma de ficha con la Speculation Rules API.

Speculation Rules API

Antes de empezar, una aclaración preload y prefetch sirven para hacer el request de recursos para que cuando se usen ya se hayan descargado (Hay mas diferencias pero quiero simplificar). Esto en archivos de assets esta perfecto pero cuando se solicitan documentos HTML estos solo se renderizarán cuando ese documento se cargue en el contexto del browser.

Si quisieramos cargar algún recurso preload y prefetch son suficientes, esto normalmente tiene mayor uso en una Single Page App (SPA), en una SPA la navegación es mas compleja porque todo se administra dentro del contexto de la aplicación. Cuando tenemos una Multi Page App (MPA) donde la navegación es simple pero se cambia todo el contexto de la aplicación. Aquí es donde Speculation Rules API nos permite especificar que URLs podemos prerenderizar para darnos una sensación de inmediates.

Advertencia

La implementación de Speculation Rules API también tiene consecuencias, al hacer un prerender nuestro JS se ejecuta. Si tenemos analytics podemos estar introduciendo ruido a nuestras métricas ya que estamos "Especulando" ya que nada nos garantiza que el usuario va a acceder a las urls que especificamos.

Adicionalmente sería bueno considerar el tipo de dispositivo y conexión

Demo

Sitio web con links a page2 y page3

Cree un sitio sencillo con 3 páginas: index, page2 y page3 (que creativo).

El servidor que cree para servir el contenido hace que cualquier URL que termine en .html tendrá un delay de 2 segundos (Esta parte es opcional y se hizo por motivos dramáticos XD)

const express = require("express");
const path = require("path");

const app = express();
const port = process.env.PORT || 9000;

// Define the static files directory
const staticPath = path.join(__dirname, "public");

// Middleware to delay response for HTML files
app.use((req, res, next) => {
  if (req.url.endsWith(".html")) {
    setTimeout(() => next(), 2000); // Delay by 2 seconds
  } else {
    next();
  }
});

// Serve static files
app.use(express.static(staticPath));

app.listen(port, () => {
  console.log(`Server listening on port ${port}`);
});
Enter fullscreen mode Exit fullscreen mode

Código Page2

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Page 2</title>
  </head>
  <body>
    <h1>Page 2</h1>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Código Page3

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Page 3</title>
  </head>
  <body>
    <h1>Page 3</h1>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Código index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Main</title>
    <script type="speculationrules">
      {
        "prerender": [
          {
            "urls": ["page2.html"]
          }
        ]
      }
    </script>
  </head>
  <body>
    <h1>Speculation API</h1>
    <h2><a href="page2.html">Page 2</a></h2>
    <h2><a href="page3.html">Page 3</a></h2>
  </body>
</html>

Enter fullscreen mode Exit fullscreen mode

La Speculation Rules API puede ser usada como un tipo de script o también como un JSON externo pero este debe ser especificado en un Header.

Speculation Rules API no es un API que pueda usarse directamente con JS, funciona como un JSON donde especificas una configuración.

Si usas Chrome, antes de ejecutar el demo te sugiero abrir las developer tools, ve a Application y busca en el panel izquierdo una sección llamada "Speculative loads". Ahí podras ver que la página se cargó (aunque igual podrías usar Network).

Speculative loads

Da click en Page 3 y después a Page 2. ¿Ves la diferencia? Ahora si, vamos a explicar al responsable de lo que acaba de suceder.

<script type="speculationrules">
      {
        "prerender": [
          {
            "urls": ["page2.html"]
          }
        ]
      }
    </script>
Enter fullscreen mode Exit fullscreen mode

En la referencia de Speculation Rules API nos especifíca que la configuración es un objeto con 2 propiedades prerender y prefetch. Por ahora nos enfocaremos en prerender.

De acuerdo a la documentación las reglas que usamos tienen propiedades como urls, un arreglo donde ponemos las URLs que queremos prerenderizar.

También existe la propiedad where para hacer una selección mas avanzada de aquello que queremos prerenderizar (lo abordaremos en otros posts)

Hay una propiedad llamada eagerness que le indica al browser "cuando" se debería hacer el prefetch/prerender de los recursos especificados en la regla.

Por defecto para lo especificado en "urls", el eagerness tiene un valor de "immediate". Lo que quiere decir que el prerendering comenzará tan pronto como sea posible. Prueba agregando esa propiedad con el valor de "moderate" (no olvides abrir las devtools para ver que sucede)

<script type="speculationrules">
      {
        "prerender": [
          {
            "urls": ["page2.html"],
            "eagerness": "moderate"
          }
        ]
      }
    </script>
Enter fullscreen mode Exit fullscreen mode

Te dirá que hay una regla que no se ha disparado

Devtools speculation rules

Ahora pon el cursor sobre el link y veras como se dispara el evento y hace el prerender. Esto es muy util para evitar prerenders innecesarios.

En futuros post abordaremos mas sobre los diferentes tipos de eagerness. Nota: tu no tienes forma de saber cuando ocurriran estos eventos de manera exacta, eso lo decide el browser.

Consideraciones

Tu script de analytics y ejecución de ciertos scripts que se deben ejecutar únicamente cuando el usuario visite el documento tendrá que tener código adicional.

Mira el siguiente código rob...inspirado en la documentación de Mozilla

if (document.prerendering) {
  document.addEventListener("prerenderingchange", initAnalytics, {
    once: true,
  });
} else {
  initAnalytics();
}
Enter fullscreen mode Exit fullscreen mode

Si bien el API no puede ser usada directamente con JS, puedes usar JS para agregarlas dinámicamente

if (
  HTMLScriptElement.supports &&
  HTMLScriptElement.supports("speculationrules")
) {
  const specScript = document.createElement("script");
  specScript.type = "speculationrules";
  const specRules = {
    prefetch: [
      {
        source: "list",
        urls: ["/next.html"],
      },
    ],
  };
  specScript.textContent = JSON.stringify(specRules);
  document.body.append(specScript);
}
Enter fullscreen mode Exit fullscreen mode

Habrá mas post, sobre esta API en el futuro, integraciones con react y como usarla en producción sin preocupaciones aunque esta API sea experimental.

Si les gusta compartan, sino pues también XD. Nos vemos en futuros posts

Top comments (0)