¿Por qué escribir su cuadrícula de datos de reacción en 2019?

Hola Habr! Estoy involucrado en el desarrollo de un sistema ECM. Y en una breve serie de artículos quiero compartir nuestra experiencia y la historia del desarrollo de mi React Data Grid (en adelante, simplemente una grilla), a saber:


  • por qué abandonamos los componentes terminados
  • ¿Qué problemas y tareas encontramos al desarrollar nuestra grilla?
  • ¿Qué beneficio da el desarrollo de su red?

Antecedentes


Nuestro sistema tiene una aplicación web en la que los usuarios trabajan con listas de documentos, resultados de búsqueda, directorios. Además, las listas pueden ser pequeñas (10 empleados) o muy grandes (50,000 contratistas). Para mostrar estas listas, desarrollamos nuestra propia cuadrícula:


imagen


Cuando comenzamos a desarrollar una aplicación web, queríamos encontrar una biblioteca preparada para mostrar una cuadrícula que pueda hacer todo lo que necesitamos: ordenar y agrupar registros, arrastrar y soltar columnas, trabajar con múltiples selecciones, filtrar y calcular totales de columnas, en porciones Descargue datos del servidor y muestre decenas de miles de registros.


Explicaré el último requisito "mostrar decenas de miles de registros". En las cuadrículas, este requisito se implementa de varias maneras: paginación, desplazamiento infinito, desplazamiento virtual.


Los enfoques de paginación y desplazamiento infinito son comunes en los sitios web, los usa todos los días. Por ejemplo, paginación en Google:


imagen


O desplazarse al infinito en el mismo Google en imágenes, donde se carga la siguiente porción de imágenes cuando se desplaza a través de la primera porción hasta el final:


imagen


Pero el desplazamiento virtual (en lo sucesivo denominado desplazamiento virtual) rara vez se usa en la web, su principal diferencia con el desplazamiento infinito es la capacidad de desplazarse rápidamente por listas muy grandes en cualquier lugar. En este caso, solo se descargarán y mostrarán los datos visibles para el usuario.


imagen


Para nuestra aplicación web, quería usar el desplazamiento virtual. Estoy de acuerdo en que desplazarse a cualquier lugar en la lista de 10,000 entradas es un caso bastante inventado. Sin embargo, el desplazamiento aleatorio dentro de 500-1000 registros es un caso en vivo.


Cuando implementan el desplazamiento virtual, a menudo implementan la API de software para administrar este desplazamiento. Esta es una característica muy importante. El desplazamiento de software se utiliza, por ejemplo, para colocar el registro seleccionado en el medio de la pantalla al abrir el directorio:


imagen


De vuelta a los requisitos. ¿Qué más necesitábamos?


  • API de gestión de desplazamiento virtual
  • Personalización de la apariencia de la cuadrícula (filas, columnas, menú contextual) para que la cuadrícula no se vea extraña en nuestra aplicación
  • Soporte para las tecnologías que utilizamos: react, redux y flexbox
  • Que la cuadrícula funcionó en ie11

En general, había muchos requisitos.


El primer intento (2016). DevExtreme JavaScript Data Grid


Sin explorar mucho las bibliotecas existentes, nos topamos con la cuadrícula de datos de JavaScript DevExtreme. Por requisitos funcionales, esta red cubría todas nuestras necesidades y tenía una apariencia muy presentable. Sin embargo, no era adecuado para requisitos tecnológicos (no reaccionar, no redux, no flexbox). En ese momento, DevExtreme no tenía una grilla de reacción.


Bueno, dejemos que no reaccione, decidimos, porque la cuadrícula es hermosa y funcional, la usaremos. Y agregaron la biblioteca a su proyecto. Resultó que agregamos 3 MB de scripts.


Durante un par de semanas, integramos la cuadrícula en nuestra aplicación web y aumentamos la funcionalidad básica:


  • Envolvió una cuadrícula para hacer amigos con react y redux
  • Desplazamiento virtual elevado y carga de porciones de datos desde nuestro servidor web
  • Implementación de clasificación y selección

En el proceso de atornillar la red, se aclararon dos problemas serios y un montón de problemas menos serios.


Primer problema grave


Hacer que DevExtreme JavaScript Data Grid con redux sea muy difícil. Logramos controlar la configuración de las columnas y resaltar los registros a través de redux, pero almacenar datos cargados en porciones en redux y realizar operaciones CRUD en ellos a través de redux; esto no es realista. Tuve que hacer una muleta que, sin pasar por redux, manipulara los datos de la cuadrícula. La muleta resultó ser compleja y frágil. Esta fue la primera alarma que la red no nos convenía, pero continuamos atornillándola.


Segundo problema serio


No hay una API de gestión de desplazamiento virtual. No pudimos rechazar el control de desplazamiento del software, tuvimos que rehacer las fuentes DevExtreme y encontrar la API de control de desplazamiento interno. Por supuesto, esta API tenía una montaña de limitaciones, porque fue diseñada para uso interno. Como resultado, logramos hacer que la API interna funcione más o menos en nuestros casos, pero nuevamente, evitando redux, y nuevamente un montón de muletas.


Problemas menos serios


Surgieron problemas menos serios todo el tiempo, porque la funcionalidad estándar de DevExtreme JavaScript Data Grid no era completamente adecuada para nosotros, e intentamos corregirla:


  1. Estirar la cuadrícula DevExtreme en altura no funciona. Tuve que escribir un truco para enseñarle a DevExtreme cómo hacer esto (tal vez no haya ningún problema con esto en versiones recientes).
  2. Cuando el foco no está en la cuadrícula, es imposible controlar la selección de líneas a través del teclado (y lo necesitábamos). Tuve que escribir el control de mi teclado.
  3. Al cambiar la composición de las columnas y los datos, tuvimos el problema de parpadear los datos (con el desplazamiento virtual habilitado).
  4. El problema de una gran cantidad de solicitudes cuando se muestra la cuadrícula por primera vez. Fue especialmente notable cuando controlamos el desplazamiento a través de la API interna.
  5. Es difícil personalizar algunas partes de la cuadrícula de la interfaz de usuario. Por ejemplo, había un deseo sobre la línea de cuadrícula seleccionada para dibujar acciones de administración de línea (eliminar una línea, copiar, abrir una tarjeta). Pero cómo atornillar esto en DevExtreme no estaba claro, e incluso usando react:
    imagen
  6. Es difícil clasificar ordenadamente (queríamos ordenar por datos que no se muestran en la cuadrícula y no se asignan en columnas).
  7. Se requieren muletas para atornillar el componente de reacción en las celdas de la rejilla (después de todo, la rejilla no está en la reacción).
  8. No se debe escribir código DevExtreme (flujo / mecanografiado).
  9. Problema de velocidad con desplazamiento virtual largo.
  10. Problema de velocidad al estirar / reorganizar columnas (después de un desplazamiento virtual prolongado).
  11. El tamaño de los scripts de cuadrícula es de 3 Mb.

Aunque la cuadrícula de funcionalidad DevExtreme contenía todo lo que necesitábamos, quería reescribir casi toda la funcionalidad estándar. Durante su uso, se agregaron cientos de líneas de código que eran difíciles de entender que intentaban resolver los problemas de interacción con redux y reaccionar, era difícil usar una cuadrícula sin reacción en una aplicación de reacción.


Denegación de DevExtreme. Busca alternativas


Después de algún tiempo usando DevExtreme, se decidió abandonarlo. Deseche todos los hacks, código complejo, así como 3 MB de scripts DevExtreme. Y busca o escribe una nueva cuadrícula.


Esta vez, estamos más atentos al estudio de las redes existentes. MS Fabric DetailsList, ReactVirtualized Grid, DevExtreme React Grid, Telerik Grid, KendoUI Grid fueron estudiados.
Los requisitos seguían siendo los mismos, pero ya se concretaron en una lista que nos quedó clara.


Requerimientos tecnológicos:


  • reaccionar
  • redux
  • flexbox

Requerimientos Funcionales:


  • Desplazamiento virtual (con la capacidad de mostrar decenas de miles de registros)
  • API de gestión de desplazamiento
  • Almacenamiento de datos y configuraciones de cuadrícula en redux
  • Carga de datos por lotes desde un servidor web
  • Gestión de columnas (estiramiento / reorganización / control de visibilidad)
  • Clasificación de columnas + filtrado
  • Selección múltiple
  • Me gusta-búsqueda con luz de fondo
  • Desplazamiento horizontal
  • Teclado
  • Menú contextual (en una línea, en un área vacía, en columnas)
  • Soporte ie11, edge, chrome, ff, safari

En este punto, la primera versión de DevExtreme React Grid ya había aparecido, pero de inmediato la descartamos por las siguientes razones:


  • El desplazamiento virtual no es compatible con ie11
  • El desplazamiento virtual no funciona junto con la descarga por lotes de datos del servidor (aunque parece haber algunas soluciones).
  • Y lo más importante, no quería pisar el mismo rastrillo cuando quería reescribir la mitad de la funcionalidad estándar de una grilla de terceros.

El análisis de las soluciones existentes mostró que no existe una "bala de plata". No existe una cuadrícula que cubra todos nuestros requisitos. Se decidió escribir nuestra propia cuadrícula, que en términos de funcionalidad desarrollaremos en la dirección que necesitamos, y seremos amigos de las tecnologías que necesita nuestro producto.


Desarrollando su cuadrícula de datos de reacción


El desarrollo de la red comenzó con prototipos, donde probaron los temas más difíciles para nosotros:


  • desplazamiento virtual
  • almacenamiento de todos los datos de la red en Redux

Desplazamiento virtual


El más difícil resultó ser el desplazamiento virtual. En su mayor parte, se realiza en una de 3 formas:


1. Virtualización de página
Los datos se dibujan en porciones - páginas. Al desplazarse, se agregan páginas visibles, se eliminan las invisibles. La página consta de 20-60 líneas (generalmente el tamaño es personalizable). Aquí es donde fueron los productos: DevExtreme JavaScript Data Grid, MS Fabric DetailsList.


imagen


2. Virtualización línea por línea
Solo se dibujan líneas visibles. Tan pronto como una línea sale de la pantalla, se elimina de inmediato. Los productos fueron así: ReactVirtualized Grid, DevExtreme React Grid, Telerik Grid.


imagen


3. Lienzo
Todas las líneas y sus contenidos se dibujan con Canvas. Esto es lo que hizo Google Docs.


imagen


Al desarrollar la cuadrícula, creamos prototipos para las tres opciones de virtualización (incluso para Canvas). Y eligieron la virtualización página por página.


¿Por qué abandonó otras opciones?


La virtualización línea por línea tuvo problemas con la velocidad de representación en el prototipo. Tan pronto como el contenido de las líneas se volvió más complicado (mucho texto, resaltado, recorte, íconos, una gran cantidad de columnas y flexbox en todas partes), se volvió costoso agregar / quitar líneas varias veces por segundo. Por supuesto, los resultados también dependen del navegador (hicimos soporte, incluido ie11, edge):


imagen


La opción de Canvas era muy seductora en la velocidad de renderizado, pero laboriosa. Se propuso dibujar todo: texto, ajuste de texto, recorte de texto, resaltado, iconos, líneas divisorias, resaltado, sangría. Haga una reacción al hacer clic en el botón del mouse en el Lienzo, resaltando las líneas cuando pasa el cursor sobre. Al mismo tiempo, algunos elementos Dom (que muestran sugerencias, "acciones emergentes" en la línea) deben aplicarse sobre Canvas. Todavía era necesario resolver el problema de difuminar texto e íconos en Canvas. Todo esto es largo y difícil de hacer. Aunque dominamos el prototipo. Al mismo tiempo, cualquier personalización de filas y celdas en el futuro resultaría en una gran laboriosidad para nosotros.


Los beneficios de la paginación


La virtualización seleccionada página por página tenía ventajas en comparación con línea por línea, lo que determinó su elección:


  • Si la página ya está representada, el desplazamiento dentro de la página es barato (el árbol DOM no cambia al desplazarse). La virtualización línea por línea con cualquier desplazamiento menor requiere cambiar el árbol DOM, y esto es costoso cuando el árbol DOM es complejo y flexbox se usa en todas partes.
  • Para listas pequeñas (<200 entradas) las páginas no se pueden eliminar, solo agregue. Tarde o temprano, se construirán todas las páginas y el desplazamiento será completamente gratuito (en términos de tiempo de representación).

Selección de tamaño de página


Un tema aparte es la elección del tamaño de página. Escribí anteriormente que el tamaño es personalizable y generalmente es de 20-60 líneas. Se dibuja una página grande durante mucho tiempo, una pequeña lleva a una visualización frecuente de una "pantalla blanca" al desplazarse. Experimentalmente, se seleccionó un tamaño de página de 25 líneas. Sin embargo, para ie11 el tamaño se ha reducido a 5 líneas. Siente que la interfaz en IE responde mejor si dibuja muchas páginas pequeñas con retrasos pequeños que una grande con un retraso grande.


Reaccionar y desplazamiento virtual


La virtualización de la página tuvo que implementarse usando react. Para hacer esto, se deben resolver varias tareas:


Tarea 1. ¿Cómo agregar / eliminar páginas mediante reaccionar al desplazarse?


Para resolver este problema, se introdujeron los siguientes conceptos:


  • modelo de página
  • vista de página

Un modelo es información sobre la cual construir una vista. Una vista es un componente Reaccionar.


imagen


De hecho, la tarea de virtualización después de esto se redujo a manipular modelos de página: almacenar una lista de modelos de página, agregar y quitar modelos al desplazarse. Y ya desde la lista de modelos a través de reaccionar construir / reconstruir la pantalla:


imagen


En el curso de la implementación, se formaron las reglas para trabajar con modelos de página:


  • Las páginas deben agregarse una a la vez. Después de cada adición, dé tiempo para dibujar. Es aceptable agregar 1 página cada 300-500 ms; esta es una situación de desplazamiento rápido. Si agrega, por ejemplo, 5 páginas a la vez, la interfaz del usuario se bloquea en su construcción.
  • Las páginas no necesitan estar en docenas. Un ejemplo de una situación problemática: se muestran 20 páginas, el usuario va a otra lista y las 20 páginas deben eliminarse a la vez. Eliminar una gran cantidad de páginas es una operación costosa; limpiar el árbol DOM tomará 1 segundo. Para evitar esto, es mejor no guardar más de 10 páginas a la vez.
  • Para cualquier manipulación de columna (reorganización, adición, eliminación, estiramiento) es mejor eliminar páginas que no son visibles para el usuario de antemano. Esto evitará la costosa reconstrucción de todas las páginas renderizadas.

Tarea 2. ¿Cómo mostrar la barra de desplazamiento?


El desplazamiento virtual supone que hay una barra de desplazamiento disponible, que tiene en cuenta el tamaño de la lista y le permite desplazarse a cualquier lugar:


imagen


¿Cómo mostrar esa barra de desplazamiento? La solución más simple es dibujar un div invisible del tamaño requerido en lugar de datos reales. Y ya encima de este div mostramos las páginas visibles:


imagen


Tarea 3. ¿Cómo controlar el tamaño de la ventana gráfica?


Viewport es el área de datos visible de la cuadrícula. ¿Por qué vigilar su tamaño? Para calcular la cantidad de páginas que deben mostrarse al usuario. Supongamos que tenemos un tamaño de página pequeño (5 líneas) y una resolución de pantalla grande (1920x1080). ¿Cuántas páginas debe mostrar el usuario para cerrar toda la ventana gráfica?


imagen


Puede resolver este problema si conoce la altura de la ventana gráfica y la altura de una página. Ahora vamos a complicar la tarea, supongamos que el usuario cambia la escala bruscamente en el navegador - establece 50%:


imagen


La situación con la escala muestra que no es suficiente averiguar el tamaño de la ventana gráfica una vez, debe controlar el tamaño. Y ahora complicaremos la tarea por completo: los elementos html no tienen un evento de cambio de tamaño, al que puede suscribirse y controlar el tamaño. Solo el objeto de la ventana ha cambiado de tamaño.


Lo primero que viene a la mente es usar un temporizador y sondear constantemente la altura del elemento html. Pero hay una solución aún mejor que vimos en DevExtreme JavaScript Data Grid: cree un iframe invisible, estírelo al tamaño de la cuadrícula y suscríbase al evento de cambio de tamaño de iframe.contentWindow:


imagen


imagen


Resumen


PD: este no es el final. En el próximo artículo contaré cómo nos hicimos amigos de redux.


Para obtener un desplazamiento virtual completo, muchas otras tareas tuvieron que ser resueltas. Pero los descritos anteriormente fueron los más interesantes. Aquí hay algunas otras tareas que también aparecen:


  • Tenga en cuenta la dirección y la velocidad de desplazamiento al agregar / eliminar páginas.
  • Tenga en cuenta los cambios de datos para minimizar la reconstrucción de los modelos de página. Por ejemplo, eliminó una línea o agregó una línea, ¿qué hacer con las páginas ya renderizadas? ¿Desechar todo o dejar un poco? Hay espacio para la optimización.
  • Al cambiar la selección, reorganice el número mínimo requerido de páginas.

Si tiene preguntas sobre la implementación, puede escribirlas en los comentarios.

Source: https://habr.com/ru/post/457504/


All Articles