Rastrea cuántas personas leen tus artículos, usando Plausible.io, Vue.js y funciones de Azure

Jeroen Bach

Jeroen Bach · Linkedin

5 min read ·

En el artículo anterior aprendiste cómo configurar Plausible, una solución de análisis respetuosa con la privacidad y asequible. En este artículo aprenderás cómo usar Plausible para recopilar el comportamiento de lectura de tus visitantes y mostrar en la parte superior de cada uno de tus artículos cuántas personas lo han leído.

Recopilar información sobre tus visitantes es genial, pero tener más información sobre cuántos de ellos realmente leen tus artículos es aún mejor. Para hacer esto, necesitamos definir el progreso que tu visitante está haciendo en algunas etapas. Más adelante convertiremos estas etapas en objetivos, lo que te proporcionará información detallada sobre qué tan bien están funcionando tus artículos.

Nuestros pasos de progreso de lectura definidos como objetivos en Plausible
Aquí ves cuántas personas abrieron, echaron un vistazo o leyeron mi primer artículo

Rastreando el comportamiento de lectura

Usaremos un "tiempo de lectura" inspirado en Medium.com. Medium.com calcula el tiempo de lectura basándose en la velocidad de lectura promedio de un adulto (que es aproximadamente 265 palabras por minuto) y divide el recuento total de palabras por este número. Entonces, si tu artículo contiene 2650 palabras, tomará a tus visitantes aproximadamente 10 minutos leerlo. Al medir el progreso, definiremos algunos pasos para indicar el progreso, cada uno representando un porcentaje del tiempo total de lectura:

  • opened (0%): aún no ha comenzado a leer
  • peeked (10%): solo se desplazó rápidamente por el artículo
  • quarter-read (25%): comenzó a leer, pero se detuvo en un cuarto
  • half-read (50%): leyó hasta la mitad
  • three-quarter-read (75%): leyó entre la mitad o todo el artículo para lectores rápidos
  • read (100%): leyó tu artículo con gran atención

También es importante tener en cuenta algunos escenarios.

Escenario 1 => el lector se distrae y ahora tu artículo está en segundo plano

Esto es fácil de mitigar, podemos usar un event listener para detectar cambios de visibilidad del documento y detener el temporizador: document.addEventListener("visibilitychange".

Escenario 2 => el lector se aleja de la computadora

Esto es un poco más difícil de detectar, pero una opción es hacer un seguimiento de otro indicador de 'lectura', que es el desplazamiento. Entonces, además del progreso del tiempo, también rastreamos el progreso del desplazamiento, y solo cuando tanto el tiempo como el desplazamiento alcanzan un cierto nivel actualizamos el progreso de lectura al siguiente estado.

Haz clic en el botón para ver cómo te está yendo leyendo esta página

Para rastrear el progreso de lectura de tus visitantes, he escrito un composable útil que puedes ver aquí: useReadProgress.ts

A medida que avanza la lectura y cambia el estado, actualizamos Plausible con eventos personalizados. Para hacer esto, he escrito otro composable, que puedes ver aquí: useReadProgressTracking.ts. Convierte los porcentajes de useReadProgress en los objetivos de seguimiento definidos y envía eventos de seguimiento a Plausible.

Crear objetivos para cada paso en Plausible

Cuando defines los pasos anteriores como objetivos en Plausible, puedes ver un embudo agradable para cada uno de tus artículos y qué tan lejos llegan tus visitantes con ellos.

Nuestros pasos de progreso de lectura definidos como objetivos en Plausible
Nuestros pasos de progreso de lectura definidos como objetivos en Plausible

Para configurar objetivos en Plausible, ve a Site Settings > Goals y agrega un objetivo para cada uno de los pasos (opened, peeked, etc).

Leyendo las estadísticas de tu página de Plausible, usando una función de Azure

Plausible proporciona una Stats API que puedes consultar para obtener, por ejemplo, las estadísticas de tus objetivos creados. Consulta el siguiente ejemplo de código, que muestra cómo consultar la API.

public async Task<PageReads> GetPageReads(string url)
{
    using var scope = _logger.BeginScope(new Dictionary<string, object> { { "url", url } });

    var uri = new Uri(url);
    var domain = uri.Host;
    var relativeUrl = uri.PathAndQuery;

    var payload = new
    {
        site_id = domain,
        metrics = new[] { "visitors" }, // Get the unique number of Reading events
        date_range = "all",
        filters = new[]{
            new List<object> { "contains", "event:page", new[] { relativeUrl } },
            new List<object> { "is", "event:goal", new[] {"read", "three-quarter-read", "half-read", "quarter-read", "peeked", "opened"} },
        },
        dimensions = new[] { "event:goal" },
    };

    var jsonPayload = JsonSerializer.Serialize(payload);
    _logger.LogInformation("Payload: {jsonPayload}", jsonPayload);

    var response = await SendRequest(jsonPayload);
    var responseContent = await response.Content.ReadAsStringAsync();
    _logger.LogInformation("Response: {responseContent}", responseContent);

    var queryResult = JsonSerializer.Deserialize<QueryResult>(responseContent);
    if (queryResult == null)
    {
        throw new InvalidOperationException("Failed to deserialize the response content.");
    }

    var resultsDict = queryResult.Results.ToDictionary(x => x.Dimensions.First(), x => x.Metrics.First());

    return new PageReads
    {
        Read = resultsDict.GetValueOrDefault("read"),
        ThreeQuarterRead = resultsDict.GetValueOrDefault("three-quarter-read"),
        HalfRead = resultsDict.GetValueOrDefault("half-read"),
        QuarterRead = resultsDict.GetValueOrDefault("quarter-read"),
        Peeked = resultsDict.GetValueOrDefault("peeked"),
        Opened = resultsDict.GetValueOrDefault("opened"),
    };
}
{
  "site_id": "{{site_id}}",
  "metrics": ["visitors"],
  "date_range": "all",
  "filters": [
    ["contains", "event:page", ["{{relative_url}}"]],
    [
      "is",
      "event:goal",
      [
        "read",
        "three-quarter-read",
        "half-read",
        "quarter-read",
        "peeked",
        "opened"
      ]
    ]
  ],
  "dimensions": ["event:goal"]
}
{
  "results": [
    { "metrics": [68], "dimensions": ["opened"] },
    { "metrics": [45], "dimensions": ["peeked"] },
    { "metrics": [40], "dimensions": ["quarter-read"] },
    { "metrics": [31], "dimensions": ["half-read"] },
    { "metrics": [21], "dimensions": ["three-quarter-read"] },
    { "metrics": [14], "dimensions": ["read"] }
  ],
  "meta": {},
  "query": {
    "site_id": "{{site_id}}",
    "metrics": ["visitors"],
    "date_range": ["2025-01-20T00:00:00+01:00", "2025-08-02T23:59:59+02:00"],
    "filters": [
      ["contains", "event:page", ["{{relative_url}}"]],
      [
        "is",
        "event:goal",
        [
          "read",
          "three-quarter-read",
          "half-read",
          "quarter-read",
          "peeked",
          "opened"
        ]
      ]
    ],
    "dimensions": ["event:goal"],
    "order_by": [["visitors", "desc"]],
    "include": {},
    "pagination": { "offset": 0, "limit": 10000 }
  }
}

Consulta la implementación completa aquí PlausibleService.cs y el proyecto completo aquí Bach.Software.API

Conclusión

Comprender qué tan profundamente tu audiencia se involucra con tu contenido es mucho más significativo que simplemente contar las visitas a la página. Con solo unas pocas líneas de código y el poder de Plausible, ahora puedes rastrear con precisión si tus lectores están hojeando o realmente sumergiéndose. Al combinar el comportamiento de desplazamiento y el tiempo de lectura, puedes ajustar tu estrategia editorial e identificar qué resuena más. Y debido a que no usa cookies, se alinea con los estándares modernos de privacidad sin sacrificar la perspectiva.

Ya seas un desarrollador solitario, un blogger o gestiones una plataforma de contenido más grande, este enfoque te da información profunda sin el peso del seguimiento invasivo. Pruébalo en tu próxima publicación, podrías sorprenderte de lo que tus lectores realmente están haciendo.

Acerca de Jeroen Bach

Soy Ingeniero de Software y Líder de Equipo con más de 15 años de experiencia profesional. Me apasiona resolver problemas complejos a través de soluciones simples y elegantes. Este blog es donde comparto técnicas y perspectivas para construir gran software, inspirado en proyectos del mundo real.

Jeroen Bach

Diseñado en Figma y construido con Vue.js, Nuxt.js y Tailwind CSS. Desplegado vía Azure Static Web App y Azure Functions. Los análisis del sitio web están impulsados por Plausible Analytics, desplegado usando Azure Kubernetes Service.