Hola Habr! Les traigo a su atención una traducción del artículo "React Fiber Architecture" de Andrew Clark .
Entrada
React Fiber es una implementación progresiva del algoritmo clave React. Esta es la culminación de un estudio de dos años realizado por el equipo de desarrollo de React.
El objetivo de Fiber es aumentar la productividad al desarrollar tareas como la animación, organizar elementos en una página y mover elementos. Su característica principal es la representación incremental: la capacidad de dividir el trabajo de representación en unidades y distribuirlas entre múltiples cuadros.
Otras características clave incluyen la capacidad de pausar, cancelar o reutilizar las actualizaciones entrantes del árbol DOM, la capacidad de priorizar diferentes tipos de actualizaciones y también la coordinación de primitivas.
Antes de leer este artículo, le recomendamos que se familiarice con los principios básicos de React:
Revisar
¿Qué es la reconciliación?
La reconciliación es un algoritmo de reacción utilizado para distinguir un árbol de elementos de otro para determinar las partes que deben reemplazarse.
Una actualización es un cambio en los datos que se utilizan para representar una aplicación React. Esto suele ser el resultado de llamar al método setState; El resultado final de renderizar el componente.
La idea clave de React API es pensar en las actualizaciones como si pudieran conducir a una representación completa de la aplicación. Esto permite que el desarrollador actúe de manera declarativa y no se preocupe por lo racional que será la transición de la aplicación de un estado a otro (de A a B, B a C, C a A, etc.).
En general, la representación de la aplicación completa para cada cambio solo funciona en las aplicaciones más tradicionales. En el mundo real, esto afecta negativamente el rendimiento. La ley incluye optimizaciones que crean una vista de representación completa sin afectar una gran parte del rendimiento. La mayoría de estas optimizaciones implican un proceso llamado reconciliación.
La reconciliación es un algoritmo detrás de lo que estamos acostumbrados a llamar "DOM virtual". La definición suena así: cuando renderiza una aplicación React, el árbol de elementos que describe la aplicación se genera en la memoria reservada. Luego, este árbol se incluye en el entorno de representación; con el ejemplo de una aplicación de navegador, se traduce en un conjunto de operaciones DOM. Cuando se actualiza el estado de la aplicación (generalmente llamando a setState), se genera un nuevo árbol. El nuevo árbol se compara con el anterior para calcular y habilitar exactamente aquellas operaciones que se necesitan para volver a dibujar la aplicación actualizada.
Aunque Fiber es una implementación cercana del reconciliador, el algoritmo de alto nivel explicado en la documentación de React será prácticamente el mismo.
Conceptos clave:
- Diferentes tipos de componentes sugieren la generación de árboles sustancialmente diferentes. React no intentará compararlos, sino que simplemente reemplazará el árbol viejo por completo.
- Las listas se distinguen usando claves. Las claves deben ser "persistentes, predecibles y únicas".
Reconciliación vs. Renderizado
El árbol DOM es uno de los entornos que React puede dibujar, el resto se puede atribuir a las vistas nativas de iOS y Android usando React Native (es por eso que Virtual Dom es un nombre poco apropiado).
La razón por la que React admite tantos objetivos es porque React está diseñado para que la reconciliación y la representación sean fases separadas. El reconciliador, trabajando, calcula qué partes del árbol han cambiado, el procesador luego usa esta información para actualizar el árbol renderizado previamente.
Esta separación significa que React DOM y React Native pueden usar sus propios mecanismos de representación cuando usan la misma herramienta de representación que se encuentra en React Core.
La fibra es una implementación rediseñada del algoritmo de reconciliación. Tiene una relación indirecta con el renderizado, mientras que los mecanismos de renderizado (renders) se pueden cambiar para admitir todas las ventajas de la nueva arquitectura.
La planificación es un proceso que determina cuándo se debe completar el trabajo.
Trabajo : cualquier cálculo que deba realizarse. El trabajo suele ser el resultado de una actualización (por ejemplo, llamando a setState).
Los principios de la arquitectura React son tan buenos que solo se pueden describir con esta cita:
En la implementación actual de React, atraviesa el árbol de forma recursiva y llama a las funciones de representación en todo el árbol actualizado en una sola marca (16 ms). Sin embargo, en el futuro, podrá cancelar algunas actualizaciones para evitar saltos de fotogramas.
Este es un tema frecuentemente discutido con respecto al diseño de React. Algunas bibliotecas populares implementan un enfoque "push", donde los cálculos se realizan cuando hay nuevos datos disponibles. Sin embargo, React se adhiere al enfoque de extracción, donde los cálculos pueden cancelarse cuando sea necesario.
React no es una biblioteca para procesar datos generalizados. Esta es una biblioteca para construir interfaces de usuario. Creemos que debe tener una posición única en la aplicación para determinar qué cálculos son adecuados y cuáles no en este momento.
Si hay algo detrás de escena, entonces podemos deshacer toda la lógica asociada con él. Si los datos llegan más rápido que la velocidad de representación de cuadros, podemos combinar las actualizaciones. Podemos aumentar la prioridad del trabajo que surge como resultado de la interacción del usuario (como la aparición de una animación cuando se presiona un botón) frente a un trabajo menos importante en segundo plano (renderizar nuevo contenido cargado desde el servidor) para evitar descargas de cuadros.
Conceptos clave:
- En las interfaces de usuario, no es importante que cada actualización se aplique de inmediato; de hecho, este comportamiento será superfluo, contribuirá a la caída de cuadros y al deterioro de UX.
- Los diferentes tipos de actualizaciones tienen diferentes prioridades: las actualizaciones de animación deben terminar más rápido que, por ejemplo, actualizar el almacenamiento de datos.
- Un enfoque basado en push requiere que la aplicación (usted, el desarrollador) decida cómo planificar el trabajo. Un enfoque basado en la extracción permite que el marco tome decisiones por usted.
Reaccionar en este momento no tiene la ventaja de planificar en gran medida; Los resultados de la actualización para todo el subárbol se dibujarán de inmediato. La idea clave de Fiber es seleccionar cuidadosamente los elementos en el algoritmo React kernel para aplicar la programación.
¿Qué es la fibra?
Discutiremos el corazón de la arquitectura React Fiber. La fibra es una abstracción de nivel inferior sobre la aplicación que los desarrolladores están acostumbrados a pensar. Si considera que sus intentos de comprenderlo no tienen esperanza, no se sienta desanimado (no está solo). Sigue buscando y finalmente dará sus frutos.
Y asi!
Hemos logrado ese objetivo principal de la arquitectura de Fiber: dejar que React aproveche la planificación. Específicamente, necesitamos poder:
- Detenga el trabajo y vuelva a él más tarde.
- priorizar diferentes tipos de trabajo.
- Reutilice el trabajo realizado anteriormente.
- cancelar el trabajo si ya no es necesario.
Para hacer todo esto, primero debemos dividir el trabajo en unidades. En cierto sentido, esto es fibra. La fibra representa una unidad de trabajo.
Para ir más allá, volvamos al concepto básico de React "componentes como datos de función" , a menudo expresados como:
v = f(d)
Con esto se deduce que representar una aplicación React es como llamar a una función cuyo cuerpo contiene llamadas a otras funciones, y así sucesivamente. Esta analogía es útil cuando se piensa en fibras.
La forma en que las computadoras básicamente verifican el orden de ejecución de un programa se denomina pila de llamadas. Cuando se completa la función, el nuevo contenedor de pila se agrega a la pila. Este contenedor de pila representa el trabajo realizado por una función.
Cuando se trabaja con interfaces de usuario, se realiza demasiado trabajo de inmediato y esto es un problema, puede provocar saltos en la animación y se verá de forma intermitente. Además, parte de este trabajo puede no ser necesario si se reemplaza por la actualización más reciente. En este punto, la comparación entre la interfaz de usuario y la función diverge, porque los componentes tienen una responsabilidad más específica que las funciones en general.
Los últimos navegadores y React Native implementan API que ayudan a resolver este problema:
requestIdleCallback distribuye tareas para que se invoquen funciones de baja prioridad en un período simple, y requestAnimationFrame distribuye tareas para que se invoquen funciones altamente priorizadas en el siguiente marco. El problema es que para usar estas API debe dividir el trabajo de representación en unidades incrementales. Si solo confía en la pila de llamadas, el trabajo continuará hasta que la pila esté vacía.
¿No sería bueno si pudiéramos personalizar el comportamiento de la pila de llamadas para optimizar la visualización de partes de la interfaz de usuario? ¿Sería bueno si pudiéramos romper la pila de llamadas para manipular contenedores manualmente?
Este es el llamado de React Fiber. Fiber es una nueva implementación de pila adaptada a los componentes React. Puede pensar en una sola fibra como un contenedor de pila virtual.
La ventaja de esta implementación de la pila es que puede guardar la pila de contenedores en la memoria y ejecutarla entonces (y donde) que desee. Esta es una definición crucial para lograr sus objetivos de planificación.
Además de la planificación, las acciones manuales con la pila revelan el potencial de conceptos tales como consistencia (concurrencia) y manejo de errores (límites de error).
En la siguiente sección, observamos la estructura de las fibras.
Estructura de fibra
Específicamente, una "fibra" es un objeto JavaScript que contiene información sobre un componente, su entrada y salida.
La fibra es consistente con el contenedor de la pila, pero también es consistente con la esencia del componente.
Estas son algunas propiedades importantes de la "fibra" (Esta lista no es exhaustiva):
Tipo y clave
El tipo y la clave sirven tanto a la fibra como a los elementos React. De hecho, cuando se crea una fibra, estos dos campos se copian directamente a ella.
El tipo de fibra describe el componente al que corresponde. Para la composición de componentes, el tipo es una función o clase de componente. Para componentes de servicio (div, span), el tipo es una cadena.
Conceptualmente, un tipo es una función cuya ejecución es rastreada por un contenedor de pila.
Junto con el tipo, la clave se usa al comparar árboles para determinar si la fibra se puede reutilizar.
Niño y hermano
Estos campos apuntan a otras fibras, que describen la estructura recursiva de las fibras.
El elemento secundario de fibra corresponde al valor que se devolvió como resultado de llamar al método de representación en el componente. En el siguiente ejemplo:
function Parent() { return <Child /> }
Parent Fiber Child corresponde a Child.
El campo relativo (o vecino) se usa si render devuelve varios elementos secundarios (una nueva función en Fiber):
function Parent() { return [<Child1 />, <Child2 />] }
Las fibras secundarias son una lista enlazada individualmente en la que se encuentra el primer elemento secundario. Entonces, en este ejemplo, el niño Parent es Child1, y los parientes de Child1 son Child2.
Volviendo a nuestra analogía con las funciones, puede pensar en una fibra secundaria como una función llamada al final (función llamada cola).
Ejemplo de Wikipedia:
function foo(data) { a(data); return b(data); }
En este ejemplo, la función llamada cola es b.
Valor de retorno (retorno)
La fibra de retorno es la fibra a la que el programa debería regresar después de procesar la fibra actual. Esto es lo mismo que devolver la dirección del contenedor de la pila.
También se puede considerar una fibra madre.
Si una fibra tiene múltiples fibras secundarias, el retorno de cada fibra secundaria devuelve la fibra primaria. En el ejemplo anterior, la fibra de retorno de Child1 y Child2 es Parent.
Propiedades actuales y en caché (pendientesProps y memorizedProps)
Conceptualmente, las propiedades son argumentos de función. Las propiedades de fibra actuales son un conjunto de estas propiedades al comienzo de la ejecución, las en caché son un conjunto al final de la ejecución.
Cuando las propiedades de espera de entrada se almacenan en caché, esto significa que la salida de fibra anterior se puede reutilizar sin ningún cálculo.
Prioridad del trabajo actual (pendienteWorkPriority)
La fibra muestra la cantidad de trabajo de determinación de prioridad. El módulo de nivel de prioridad en React ReactPrioritylevel incluye diferentes niveles de prioridad y lo que representan.
Comenzando con una excepción del tipo NoWork, que es 0, un número más alto define la prioridad más baja. Por ejemplo, puede usar la siguiente función para verificar si la prioridad de la fibra es mayor que el nivel especificado:
function matchesPriority(fiber, priority) { return fiber.pendingWorkPriority !== 0 && fiber.pendingWorkPriority <= priority }
Esta función es solo para fines ilustrativos; no forma parte de la base de datos React Fiber.
El planificador utiliza el campo de prioridad para encontrar la siguiente unidad de trabajo que se puede realizar. Discutiremos este algoritmo en la siguiente sección.
Alternativa (o par)
Actualización de fibra (descarga): esto significa mostrar su salida en la pantalla.
Fibra en desarrollo (trabajo en progreso): fibra que aún no se ha construido; en otras palabras, es un contenedor de pila que aún no se ha devuelto.
En cualquier momento, la esencia del componente no tiene más de dos estados para la fibra que corresponde a: fibra en el estado actual, fibra actualizada o fibra en desarrollo.
La fibra actual es seguida por la fibra que se está desarrollando, y luego, a su vez, la fibra se actualiza.
El siguiente estado de fibra se crea perezosamente utilizando la función cloneFiber. Casi siempre al crear un nuevo objeto, cloneFiber intentará reutilizar una alternativa (par) de fibra si existe, mientras minimiza el costo de los recursos.
Debería pensar en el campo de vapor (o alternativa) como un detalle de implementación, pero aparece tan a menudo en la documentación que era simplemente imposible no mencionarlo.
La conclusión es un elemento de servicio (o un conjunto de elementos de servicio); nodos hoja Reaccionar aplicaciones. Son específicos para cada entorno de visualización (por ejemplo, en un navegador es 'div', 'span', etc.). En JSX, se denotan como nombres de etiqueta en minúsculas.
En pocas palabras: recomiendo probar las características de la nueva arquitectura React v16.0