Blog.

Speculation Rules API

Nicolas Bello Camilletti
Nicolas Bello Camilletti
5 min read

Speculation Rules API

Hace tiempo se trabaja en formas de optimizar los tiempos de respuestas de las siguientes páginas antes de que realmente ocurra la navegación. Este fue el caso NoState Prefetch con <link rel="prerender" href="... que permite especificar que el recurso se cargue antes de navegar, pero no preprocesa la página ni ejecuta el JavaScript por completo. Esto hace que por más que sea una experiencia más rápida que traer todos los recursos cuando se navega, no sea instantánea.

En ese contexto, es que aparece Speculation Rules API, una API simple pero poderosa que permite pre cargar sitios de la misma forma que ocurre cuando se escribe una url en la barra de direcciones (y chrome cree con un alto grado de certeza que vas a navegar a esa url) o cuando usas la barra de favoritos. En estos casos, la renderización se realiza similar a cuando abrimos una pestaña invisible en segundo plano y, luego, se “activa” reemplazando la pestaña en primer plano por esa página renderizada previamente.

Como se usa

El uso es muy simple, se crea un elemento script con type speculationrules y se configura por un JSON que URLs el navegador debería renderizar previamente usando una lista de urls.

<script type="speculationrules">
{
  "prerender": [
    {
      "urls": ["next.html", "next2.html"]
    }
  ]
}
</script>

Y para mayor comodidad y flexibilidad podemos usar document rules en lugar de list rules que nos permite especificar usar una condición where y usar wildcards y otros patrones, incluyendo el uso de selectores css para especificar un conjunto de páginas.

<script type="speculationrules">
{
  "prerender": [{
    "where": {
      "and": [
        { "href_matches": "/*" },
        { "not": {"href_matches": "/logout/*"}},
        { "not": {"href_matches": "/*\\?*(^|&)add-to-cart=*"}},
        { "not": {"selector_matches": ".do-not-prerender"}},
        { "not": {"selector_matches": "[rel~=nofollow]"}}
      ]
    },
  }]
}
</script>

También existe el parámetro eagerness para especificar cuando se activa el comportamiento. Con list rules, se usa inmediate por default que se ejecuta ni bien se observen las reglas. En el caso de document rules, se usa conservative que se activa cuando el puntero esté sobre el link que cumpla la condición. Hay que tener en cuenta que con conservative el límite de páginas prerrenderizadas es de 2 (FIFO) mientas que con inmediate es de 10.

Aparte de hacer prerendering podemos hacer prefetching (pedir los documentos, pero no hacer rendering) siguiendo una estructura similar, pero remplazando la property prerender por prefetch. Incluso podemos usar ambos al mismo tiempo.

<script type="speculationrules">
{
  "prerender": [{
    "where": {
      "and": [
        { "href_matches": "/*" },
        { "not": {"href_matches": "/logout/*"}},
        { "not": {"href_matches": "/*\\?*(^|&)add-to-cart=*"}},
        { "not": {"selector_matches": ".do-not-prerender"}},
        { "not": {"selector_matches": "[rel~=nofollow]"}}
      ]
    },
  }],
 "prefetch": [{
    "urls": ["next.html", "next2.html"],
  }]
}
</script>

Usando feature detection y probando en producción

Algo interesante es que podemos ejecutar esto como un script de js y ejecutarlo en la consola para ver cómo se va a comportar en nuestro sitio. Para ver si tenemos disponible la feature podemos usar el siguiente código. También podríamos usar tag manager en caso de tener configurado.

if (
  HTMLScriptElement.supports &&
  HTMLScriptElement.supports("speculationrules")
) {
  console.log("Your browser supports the Speculation Rules API.");
}

Luego podemos ejecutar lo siguiente, que crea el elemento script y configura la speculation rule si es que existe el soporte. Obviamente, podemos cambiar la configuración de la condición según lo que necesitemos.

if (
  HTMLScriptElement.supports &&
  HTMLScriptElement.supports("speculationrules")
) {
  const specScript = document.createElement("script");
  specScript.type = "speculationrules";
  const specRules = {
    prerender: [
      {
        "where": {
          "and": [
            { "href_matches": "/*" },
            { "not": {"selector_matches": "[rel~=nofollow]"}}
          ]
        },
      },
    ],
  };
  specScript.textContent = JSON.stringify(specRules);
  document.body.append(specScript);
}

Conclusiones

Creo que lo más interesante que tiene esta API es lo simple que es para los grandes beneficios que obtenemos. Podemos acelerar las navegaciones en nuestro sitio sin necesidad de grandes cambios estructurales. Hay que considerar que con esta API también tenemos algunas restricciones, por ejemplo, por default, estos comportamientos están restringidos a páginas en el mismo origen. Aparte, tenemos que considerar las consecuencias de hacer un pre-render de nuestras páginas, como se pueden ver afectadas ciertas funcionalidades, que por más que podamos identificar cuando se trate de prerenders, no deja de complejizar nuestro código.


More Stories

Cancelando procesos asincrónicos en JavaScript

6 min read

En operaciones asincrónicas normalmente pasa que necesitamos cancelar la misma mientras que estamos esperando la respuesta. El caso más…

Nicolas Bello Camilletti
Nicolas Bello Camilletti

Using letsencrypt with nginx on docker

4 min read

Now that I have my site running on a docker container using nginx (more info here), I want to add a secure endpoint and support https. In…

Nicolas Bello Camilletti
Nicolas Bello Camilletti