Saltearse al contenido

Modelo de Concurrencia

Owl fue diseñado desde el principio con componentes asincrónicos. Esto proviene de los ganchos de ciclo de vida willStart y willUpdateProps. Con estos ganchos asincrónicos, es posible crear aplicaciones complejas y altamente concurrentes.

El modo concurrente de Owl tiene varias ventajas: permite retrasar la renderización hasta que se complete alguna operación asincrónica, permite cargar bibliotecas de forma diferida, manteniendo la pantalla anterior completamente funcional. También es bueno por razones de rendimiento: Owl lo usa para aplicar el resultado de muchas renderizaciones diferentes solo una vez en un cuadro de animación. Owl puede cancelar una renderización que ya no es relevante, reiniciarla, reutilizarla en algunos casos.

Pero aunque el uso de la concurrencia es bastante simple (y es el comportamiento predeterminado), la asincronía es difícil, porque introduce una dimensión adicional que aumenta enormemente la complejidad de una aplicación. En esta sección se explicará cómo Owl gestiona esta complejidad y cómo funciona la representación concurrente de forma general.

Rendering Components

La palabra renderizado es un poco vaga, así que vamos a explicar con más precisión el proceso mediante el cual los componentes de Owl se muestran en una pantalla.

Cuando se monta o actualiza un componente, se inicia una nueva renderización. Tiene dos fases: renderización virtual y parcheo.

Renderización virtual

Esta fase representa el proceso de renderizar una plantilla en memoria que crea una representación virtual del componente deseado (html). El resultado de esta fase es un DOM virtual.

Es asincrónico: cada subcomponente debe crearse (por lo tanto, se deberá llamar a willStart) o actualizarse (lo que se hace con el método willUpdateProps). Este es un proceso completamente recursivo: un componente es la raíz de un árbol de componentes y cada subcomponente debe representarse (virtualmente).

Parcheo

Una vez que se completa una representación, se aplicará en el siguiente cuadro de animación. Esto se hace de manera sincrónica: todo el árbol de componentes se conecta al DOM real.

Semántica

Aquí se ofrece una descripción informal de la forma en que se crean o actualizan los componentes en una aplicación. Aquí, las listas ordenadas describen acciones que se ejecutan de forma secuencial, mientras que las listas con viñetas describen acciones que se ejecutan en paralelo.

Scenario 1: renderizado inicial Imaginemos que queremos renderizar el siguiente árbol de componentes:

A
/ \
B C
/ \
D E

Esto es lo que sucede cada vez que montamos el componente raíz (con algún código como app.mount(document.body)).

  1. willStart se llama en A

  2. Cuando termine, se renderizará la plantilla «A».

    • Se crea el componente B
      1. willStart se llama en B
      2. Se renderiza la plantilla B
    • Se crea el componente C
      1. willStart se llama en C
      2. Se renderiza la plantilla C
        • Se crea el componente D
          1. willStart se llama en D
          2. Se renderiza la plantilla D
        • Se crea el componente E
          1. willStart se llama en E
          2. Se renderiza la plantilla E
  3. Cada componente se integra en un elemento DOM independiente, en el siguiente orden: E, D, C, B, A. (De esta forma, el árbol DOM completo se crea en una sola pasada)

  4. El elemento raíz del componente A en realidad se agrega a document.body

  5. El método mounted se llama recursivamente en todos los componentes en el siguiente orden: E, D, C, B, A.

Scenario 2: Actualizar un componente. Ahora, supongamos que el usuario hizo clic en algún botón en “C”, y esto genera una actualización de estado, que se supone que debe:

  • actualiza D,
  • elimina E,
  • agrega un nuevo componente F.

Entonces, el árbol de componentes debería verse así:

A
/ \
B C
/ \
D F

Esto es lo que hará Owl:

  1. Debido a un cambio de estado, se llama al método render en C

  2. La plantilla C se vuelve a representar

    • El componente D se actualiza:
      1. El gancho willUpdateProps se llama en D (asincrónico)
      2. La plantilla D se vuelve a renderizar
    • Se crea el componente F:
      1. El gancho willStart se llama en F (asincrónico)
      2. Se representa la plantilla F
  3. Los ganchos willPatch se llaman recursivamente en los componentes C, D (no en F, porque aún no está montado)

  4. Los componentes F, D se parchean en ese orden

  5. El componente C está parcheado, lo que provocará recursivamente:

    1. Gancho willUnmount en E
    2. destrucción de E,
  6. El gancho mounted se llama en F, los ganchos patched se llaman en D, C

Las etiquetas son pequeñas ayudas que facilitan la escritura de plantillas en línea. Actualmente solo hay una etiqueta disponible: xml.

Renderizado asincrónico

Trabajar con código asincrónico siempre añade mucha complejidad a un sistema. Siempre que distintas partes de un sistema están activas al mismo tiempo, es necesario pensar detenidamente en todas las interacciones posibles. Evidentemente, esto también es cierto para los componentes Owl.

Existen dos problemas comunes diferentes con el modelo de representación asincrónica de Owl:

  • Cualquier componente puede retrasar la representación (inicial y posterior) de toda la aplicación.
  • Para un componente determinado, existen dos situaciones independientes que activarán una nueva representación asincrónica: un cambio en el estado o un cambio en las propiedades. Estos cambios pueden realizarse en momentos diferentes y Owl no tiene forma de saber cómo conciliar las representaciones resultantes.

A continuación se ofrecen algunos consejos sobre cómo trabajar con componentes asincrónicos:

  1. ¡Minimiza el uso de componentes asincrónicos!
  2. La carga diferida de bibliotecas externas es un buen caso de uso para la renderización asincrónica. Esto es aceptable en la mayoría de los casos, porque podemos suponer que solo tomará una fracción de segundo y solo una vez.