Pourquoi écrire votre grille de données React en 2019

Bonjour, Habr! Je suis impliqué dans le développement d'un système ECM. Et dans une courte série d'articles, je veux partager notre expérience et l'histoire du développement de ma React Data Grid (ci-après simplement une grille), à ​​savoir:


  • pourquoi nous avons abandonné les composants finis
  • quels problèmes et tâches avons-nous rencontrés lors du développement de notre grille
  • quel bénéfice le développement de votre réseau apporte

Contexte


Notre système dispose d'une application Web dans laquelle les utilisateurs travaillent avec des listes de documents, des résultats de recherche, des répertoires. De plus, les listes peuvent être petites (10 employés) ou très grandes (50 000 entrepreneurs). Pour afficher ces listes, nous avons développé notre propre grille:


image


Lorsque nous avons commencé à développer une application Web, nous voulions trouver une bibliothèque prête à l'emploi pour afficher des grilles qui peuvent faire tout ce dont nous avons besoin: trier et grouper les enregistrements, glisser-déposer des colonnes, travailler avec plusieurs sélections, filtrer et calculer les totaux des colonnes, par portions Téléchargez les données du serveur et affichez des dizaines de milliers d'enregistrements.


J'expliquerai la dernière exigence «afficher des dizaines de milliers d'enregistrements». Dans les grilles, cette exigence est implémentée de plusieurs manières: pagination, défilement infini, défilement virtuel.


Les approches de pagination et de défilement à l'infini sont courantes sur les sites Web, vous les utilisez tous les jours. Par exemple, la pagination dans Google:


image


Ou défilement à l'infini dans le même Google sur les images, où la partie suivante des images est chargée lorsque vous faites défiler la première partie jusqu'à la fin:


image


Mais le défilement virtuel (ci-après dénommé défilement virtuel) est rarement utilisé sur le Web, sa principale différence avec le défilement à l'infini est la possibilité de parcourir rapidement de très grandes listes n'importe où. Dans ce cas, seules les données visibles par l'utilisateur seront téléchargées et affichées.


image


Pour notre application Web, je voulais utiliser le défilement virtuel. Je suis d'accord que faire défiler n'importe où dans la liste des 10 000 entrées est un cas plutôt inventé. Cependant, le défilement aléatoire dans les 500 à 1 000 enregistrements est un cas réel.


Lorsqu'ils implémentent le défilement virtuel, ils implémentent souvent l'API logicielle pour gérer ce défilement. C'est une caractéristique très importante. Le défilement logiciel est utilisé, par exemple, pour positionner l'enregistrement sélectionné au milieu de l'écran lors de l'ouverture du répertoire:


image


Retour aux exigences. De quoi d'autre avions-nous besoin:


  • API de gestion du défilement virtuel
  • Personnalisation de l'apparence de la grille (lignes, colonnes, menu contextuel) pour que la grille ne soit pas étrangère dans notre application
  • Prise en charge des technologies que nous utilisons: react, redux et flexbox
  • Que la grille a fonctionné dans ie11

En général, il y avait de nombreuses exigences.


La première tentative (2016). Grille de données JavaScript DevExtreme


Peu de temps après avoir exploré les bibliothèques existantes, nous sommes tombés sur la grille de données JavaScript DevExtreme. Par exigences fonctionnelles, cette grille couvrait tous nos besoins et avait une apparence très présentable. Cependant, il n'était pas adapté aux exigences technologiques (pas de réaction, pas de redux, pas de flexbox). À cette époque, DevExtreme n'avait pas de grille de réaction.


Eh bien, laissez-le ne pas réagir, nous avons décidé, car la grille est belle et fonctionnelle, nous allons l'utiliser. Et ils ont ajouté la bibliothèque à leur projet. Il s'est avéré que nous avons ajouté 3 Mo de scripts.


Pendant quelques semaines, nous avons intégré la grille dans notre application Web et augmenté les fonctionnalités de base:


  • Enveloppé d'une grille pour se faire des amis avec réagir et redux
  • Augmentation du défilement virtuel et du chargement des portions de données à partir de notre serveur Web
  • Tri et sélection mis en œuvre

Dans le processus de vissage de la grille, deux problèmes graves sont devenus évidents et tout un tas de problèmes moins graves.


Premier problème grave


Pour rendre la grille de données JavaScript DevExtreme avec redux très difficile. Nous avons réussi à contrôler les paramètres de la colonne et à mettre les enregistrements en surbrillance via redux, mais à stocker des données chargées en partie dans redux et à effectuer des opérations CRUD sur eux via redux - ce n'est pas réaliste. J'ai dû faire une béquille qui, en contournant le redux, a manipulé les données du réseau. La béquille s'est avérée complexe et fragile. Ce fut la première sonnette d'alarme que la grille ne nous convenait pas, mais nous avons continué à la visser.


Deuxième problème grave


Il n'y a pas d'API de gestion du défilement virtuel. Nous ne pouvions pas refuser le contrôle logiciel du défilement, nous avons dû refaire les sources DevExtreme et trouver l'API de contrôle de défilement interne. Bien sûr, cette API avait une montagne de limitations, car elle a été conçue pour un usage interne. En conséquence, nous avons réussi à faire plus ou moins travailler l'API interne sur nos boîtiers, mais encore une fois, en contournant le redux, et encore un tas de béquilles.


Problèmes moins graves


Des problèmes moins graves surgissaient tout le temps, car la fonctionnalité standard de grille de données JavaScript DevExtreme ne nous convenait pas complètement, et nous avons essayé de la corriger:


  1. L'étirement de la grille DevExtreme en hauteur ne fonctionne pas. J'ai dû écrire un hack pour apprendre à DevExtreme comment faire (peut-être qu'il n'y a pas de problème avec cela dans les versions récentes).
  2. Lorsque le focus n'est pas dans la grille, il est impossible de contrôler la sélection des lignes via le clavier (et nous en avions besoin). J'ai dû écrire ma commande de clavier.
  3. Lors de la modification de la composition des colonnes et de la modification des données, nous avons eu le problème de clignoter les données (avec le défilement virtuel activé).
  4. Le problème d'un grand nombre de requêtes lors du premier affichage de la grille. C'était particulièrement visible lorsque nous contrôlions le défilement de l'API interne.
  5. Il est difficile de personnaliser certaines parties de la grille de l'interface utilisateur. Par exemple, il y avait un désir au-dessus de la ligne de grille sélectionnée de dessiner des actions de gestion de ligne (supprimer une ligne, copier, ouvrir une carte). Mais comment visser cela dans DevExtreme n'était pas clair, et même en utilisant react:
    image
  6. Il est difficile de catomiser le tri (nous voulions trier par des données qui ne sont pas affichées dans la grille et non mappées en colonnes).
  7. Des béquilles sont nécessaires pour visser le composant de réaction dans les cellules de la grille (après tout, la grille n'est pas sur la réaction).
  8. Pas de frappe de code DevExtreme (flux / tapuscrit).
  9. Problème de vitesse avec un long défilement virtuel.
  10. Problème de vitesse lors de l'étirement / de la réorganisation des colonnes (après un défilement virtuel prolongé).
  11. La taille des scripts de grille est de 3 Mo.

Bien que la grille de fonctionnalités DevExtreme contienne tout ce dont nous avions besoin, mais je voulais réécrire presque toutes les fonctionnalités standard. Lors de son utilisation, des centaines de lignes de code difficiles à comprendre ont été ajoutées qui tentaient de résoudre les problèmes d'interaction avec redux et de réagir, il était difficile d'utiliser une grille non-réactive dans une application React.


Refus de DevExtreme. Recherche d'alternatives


Après un certain temps à utiliser DevExtreme, il a été décidé de l'abandonner. Jetez tous les hacks, le code complexe, ainsi que 3 Mo de scripts DevExtreme. Et trouvez ou écrivez une nouvelle grille.


Cette fois, nous sommes plus attentifs à l'étude des grilles existantes. MS Fabric DetailsList, ReactVirtualized Grid, DevExtreme React Grid, Telerik Grid, KendoUI Grid ont été étudiés.
Les exigences sont restées les mêmes, mais elles ont déjà pris forme dans une liste qui nous était claire.


Exigences technologiques:


  • réagir
  • redux
  • flexbox

Exigences fonctionnelles:


  • Défilement virtuel (avec la possibilité d'afficher des dizaines de milliers d'enregistrements)
  • API de gestion du défilement
  • Stockage des données et des paramètres de grille dans redux
  • Chargement de données par lots à partir d'un serveur Web
  • Gestion des colonnes (étirement / réorganisation / contrôle de la visibilité)
  • Tri + filtrage des colonnes
  • Sélection multiple
  • Recherche similaire avec rétroéclairage
  • Défilement horizontal
  • Clavier
  • Menu contextuel (sur une ligne, sur une zone vide, sur des colonnes)
  • Prise en charge ie11, edge, chrome, ff, safari

À ce stade, la première version de DevExtreme React Grid était déjà apparue, mais nous l'avons immédiatement abandonnée pour les raisons suivantes:


  • Le défilement virtuel n'est pas pris en charge dans ie11
  • Le défilement virtuel ne fonctionne pas en conjonction avec le téléchargement de données par lots à partir du serveur (bien qu'il semble y avoir quelques solutions).
  • Et surtout, je ne voulais pas marcher sur le même râteau quand je voulais réécrire la moitié des fonctionnalités standard d'une grille tierce.

L'analyse des solutions existantes a montré qu'il n'y a pas de «solution miracle». Une grille qui couvrirait toutes nos exigences n'existe pas. Il a été décidé d'écrire notre propre grille, que nous allons développer en termes de fonctionnalités dans la direction dont nous avons besoin, et d'être amis avec les technologies nécessaires à notre produit.


Développement de votre grille de données React


Le développement de la grille a commencé avec des prototypes, où ils ont testé les sujets les plus difficiles pour nous:


  • défilement virtuel
  • stockage de toutes les données du réseau dans Redux

Défilement virtuel


Le plus difficile s'est avéré être le défilement virtuel. Pour la plupart, il est fait de 3 façons:


1. Virtualisation des pages
Les données sont dessinées en portions - pages. Lors du défilement, des pages visibles sont ajoutées, des pages invisibles sont supprimées. La page se compose de 20 à 60 lignes (généralement la taille est personnalisable). C'est là que les produits sont allés: DevExtreme JavaScript Data Grid, MS Fabric DetailsList.


image


2. Virtualisation ligne par ligne
Seules les lignes visibles sont dessinées. Dès qu'une ligne quitte l'écran, elle est immédiatement supprimée. Les produits ont suivi cette voie: ReactVirtualized Grid, DevExtreme React Grid, Telerik Grid.


image


3. Toile
Toutes les lignes et leur contenu sont dessinés à l'aide de Canvas. C'est ce que Google Docs a fait.


image


Lors du développement de la grille, nous avons réalisé des prototypes pour les trois options de virtualisation (même pour Canvas). Et ils ont choisi la virtualisation page par page.


Pourquoi abandonné d'autres options?


La virtualisation ligne par ligne a rencontré des problèmes de vitesse de rendu dans le prototype. Dès que le contenu des lignes est devenu plus compliqué (beaucoup de texte, surlignage, découpage, icônes, un grand nombre de colonnes et partout flexbox), il est devenu coûteux d'ajouter / supprimer des lignes plusieurs fois par seconde. Bien sûr, les résultats dépendent également du navigateur (nous avons pris en charge, y compris pour ie11, edge):


image


L'option Canvas était très séduisante dans la vitesse de rendu, mais laborieuse. Il a été proposé de tout dessiner: texte, habillage de texte, rognage de texte, mise en surbrillance, icônes, lignes de division, mise en surbrillance, indentation. Faites une réaction en cliquant sur le bouton de la souris sur le canevas, en mettant en évidence les lignes lorsque vous survolez. Dans le même temps, certains éléments Dom (montrant des astuces, des «actions contextuelles» sur la ligne) doivent être appliqués sur Canvas. Il fallait encore résoudre le problème de flou du texte et des icônes dans Canvas. Tout cela est long et difficile à faire. Bien que nous maîtrisions le prototype. Dans le même temps, toute personnalisation des lignes et des cellules à l'avenir entraînerait une grande pénibilité pour nous.


Les avantages de la pagination


La virtualisation page par page sélectionnée présente des avantages par rapport à la ligne par ligne, ce qui détermine son choix:


  • Si la page est déjà rendue, le défilement à l'intérieur de la page est bon marché (l'arborescence DOM ne change pas lors du défilement). La virtualisation ligne par ligne pour tout défilement mineur nécessite de modifier l'arborescence DOM, ce qui est coûteux lorsque l'arborescence DOM est complexe et que Flexbox est utilisé partout.
  • Pour les petites listes (<200 entrées) les pages ne peuvent pas être supprimées, il suffit d'ajouter. Tôt ou tard, toutes les pages seront construites et le défilement sera totalement gratuit (en termes de temps de rendu).

Sélection du format de page


Un autre problème est le choix de la taille de la page. J'ai écrit ci-dessus que la taille est personnalisable et est généralement de 20 à 60 lignes. Une grande page est dessinée pendant longtemps, une petite conduit à un affichage fréquent d'un "écran blanc" lors du défilement. Expérimentalement, une taille de page de 25 lignes a été sélectionnée. Cependant, pour ie11, la taille a été réduite à 5 lignes. On dirait que l'interface dans IE est plus réactive si vous dessinez beaucoup de petites pages avec de petits retards qu'une grande avec un gros retard.


React et défilement virtuel


La virtualisation des pages devait être implémentée à l'aide de react. Pour ce faire, plusieurs tâches doivent être résolues:


Tâche 1. Comment ajouter / supprimer des pages en réagissant lors du défilement?


Pour résoudre ce problème, les concepts suivants ont été introduits:


  • modèle de page
  • vue page

Un modèle est une information sur laquelle construire une vue. Une vue est un composant React.


image


En fait, la tâche de virtualisation après cela se résumait à manipuler des modèles de page: stocker une liste de modèles de page, ajouter et supprimer des modèles lors du défilement. Et déjà à partir de la liste des modèles via react construire / reconstruire l'affichage:


image


Au cours de la mise en œuvre, les règles de travail avec les modèles de page ont été formées:


  • Les pages doivent être ajoutées une par une. Après chaque ajout, donnez le temps de dessiner. Il est acceptable d'ajouter 1 page tous les 300 à 500 ms - c'est une situation de défilement rapide. Si vous ajoutez, par exemple, 5 pages à la fois, l'interface de l'utilisateur se bloque sur leur construction.
  • Les pages n'ont pas besoin d'être conservées par dizaines. Un exemple de situation problématique: 20 pages sont affichées, l'utilisateur passe à une autre liste et les 20 pages doivent être supprimées en même temps. La suppression d'un grand nombre de pages est une opération coûteuse; le nettoyage de l'arborescence DOM prendra 1 seconde. Pour éviter cela, il est préférable de ne pas conserver plus de 10 pages à la fois.
  • Pour toute manipulation de colonne (réarrangement, ajout, suppression, étirement), il est préférable de supprimer les pages qui ne sont pas visibles à l'avance par l'utilisateur. Cela évitera une reconstruction coûteuse de toutes les pages rendues.

Tâche 2. Comment afficher la barre de défilement?


Le défilement virtuel suppose qu'une barre de défilement est disponible, qui prend en compte la taille de la liste et vous permet de faire défiler n'importe où:


image


Comment afficher une telle barre de défilement? La solution la plus simple consiste à dessiner un div invisible de la taille requise au lieu de données réelles. Et déjà au dessus de cette div on affiche les pages visibles:


image


Tâche 3. Comment surveiller la taille de la fenêtre?


La fenêtre est la zone de données visible de la grille. Pourquoi garder un œil sur sa taille? Pour calculer le nombre de pages qui doivent être affichées pour l'utilisateur. Supposons que nous ayons une petite page (5 lignes) et une grande résolution d'écran (1920x1080). Combien de pages l'utilisateur doit-il afficher pour fermer la fenêtre entière?


image


Vous pouvez résoudre ce problème si vous connaissez la hauteur de la fenêtre d'affichage et la hauteur d'une page. Maintenant, compliquons la tâche, supposons que l'utilisateur modifie fortement l'échelle dans le navigateur - définit 50%:


image


La situation avec l'échelle montre qu'il ne suffit pas de connaître la taille de la fenêtre une fois, vous devez surveiller la taille. Et maintenant, nous allons compliquer complètement la tâche: les éléments html n'ont pas d'événement de redimensionnement, auquel vous pouvez vous abonner et surveiller la taille. Seul l'objet fenêtre a été redimensionné.


La première chose qui vient à l'esprit est d'utiliser une minuterie et d'interroger constamment la hauteur de l'élément html. Mais il existe une solution encore meilleure que nous avons vue dans la grille de données JavaScript DevExtreme: créer un iframe invisible, l'étirer à la taille de la grille et vous abonner à l'événement resize de iframe.contentWindow:


image


image


Résumé


PS Ce n'est pas la fin. Dans le prochain article, je dirai comment nous nous sommes liés d'amitié avec redux.


Pour obtenir un défilement virtuel complet, de nombreuses autres tâches devaient être résolues. Mais ceux décrits ci-dessus étaient les plus intéressants. Voici quelques autres tâches qui apparaissent également:


  • Tenez compte du sens et de la vitesse de défilement lorsque vous ajoutez / supprimez des pages.
  • Tenez compte des modifications des données pour minimiser les modèles de page de reconstruction. Par exemple, supprimé une ligne ou ajouté une ligne, que faire des pages déjà rendues? Jeter tout ou en laisser? Il y a place pour l'optimisation.
  • Lorsque vous modifiez la sélection, réorganisez le nombre minimum de pages requis.

Si vous avez des questions sur la mise en œuvre, vous pouvez les écrire dans les commentaires.

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


All Articles