Bonjour,
je m'interroge, investis du temps et fais quelques tests de faisabilité autour d'une bibliothèque de gestion de DOM virtuel "maison". J'ai besoin d'avis de personnes agueries aux structures de données complexes et autres frameworks tels que React / Redux / Preact / Skatejs.
Ce post est assez long, je m'efforce de le segmenter au mieux. Une simple remarque ou un encouragement peuvent être utiles donc n'hésitez pas...
Mon idée de base est de stocker l'arborescence du markup de l'appli (nieuds et attributs pour commencer) dans un pool linéaire unique de blocks identiques. Les opérations de diff / patch se feraient à partir de cette structure unique. Ce sont des modifications "in-place" mais en mémoire, pas dans le DOM, à part le moment de patcher.
Pourquoi cela :
- pour faciliter la persistance dans une BDD locale ou distante de blocs identiques (facilité de sérialisation)
- pour éviter / minimiser les cycles de garbage collector inhérents à chaqeue création d'arbre + destruction du précédent
- pour permettre le time-travelling de l'application : possibilité de rejouer des scénatrios, de mettre en pause en cas d'erreur, etc.
- parce que c'est un excelllent exercice de style et que c'est cool de tenter !
EDIT: accessoirement cela permettrait de stocker l'image (au sens sérialisation) applicative à un instant T. Du coup dans une application hybride de type Cordova; cela permettrait de détecter l'obsolescence de l'appli et de faire sa mise à jour sans passer ni par le store d'appli, ni par un quelconque service tiers de code psuh... le conteneur n'aura qu'à requêter de temps à autre le serveur (une fois par semaine ou tous les x utilisations...) en comparant le hash de la version courante, et celle qu'il a en cache... et le tour sera joué (attention #yakafaucon !)
Je ne suis pas un grand expert de la programation fonctionnelle réactive, aussi mes tests sont ils exprimés de façon impérative.
Concrètement j(ai défini une unité de base, le BLOCK objet "propeerty bag" ayant pour signature :
EDIT: le tri topologique de l'arbre pour permettre son affichage text dans une console par exemple se ferait beaucoup plus simplement avec l'ID du premier ATTRibut, l'ID du premier fils et l'ID du prochain de fratrie =... cela fait 3 clés * 4 octets = 12 octets de plus par bloc !
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 let block = ( ) => { UID : ..., // identifiant unique dans le blocks pool PUID : ... // identifiant du block parent kind: ... // ce qu'in stocke dans ce block TAG, TEXT, ATTRIBUTE, CODE .. rank: 0 // rang dans la fratrie (child sibling rank) name: 'div' // nom de l'élément stocké. Pour les tags par exemple, et les attributs value: '' // valuer pour les attributs et les noeuds texte ou code used: true // idndique si le bloc est à utluser ou à recycler }
Le pool est un autre object bag contenant :
- une liste de blocks "blocks", utilsés ou non, et de taille variable, pouvant croître de 25% à chaque réallocation;
- une entrée "lookup" : hash object ou une map dont les clés sont des UIDs et les valeurs des blocks, permet de garantir un temps d'accès constant aux blocks de la liste, au prix d'un léger surcout de mémoire que je pense pouvoir compenser par la suite grâce au côté in-place des traitements
- une liste "reldased" stockant et permettant de dépiler (pop) des blocs libres quand c'est nécessaire
Le pool a donc basiquement cette structure :
Il y aura surement quelque part un éspece de swap (pile ou queue) pour, manoeuvrer temportairement les blocs lors des traitements.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 const pool = { blocks: [], lookup : {}, released : [] }
Comme les blocks sont effectivement stockés dans la liste, le surcôut mémoire provient essentuiellement des clés de la lookup... un bloc créé dans la liste est seulement référencé dans la lookup et peuit etre dans la liste "released" des blocs libres.
Les UIDs sont des clés de 4 digits sur de 6 bits (64 valuers) = 24 bits en tout, lisibles et compatibles avec un identifiant ( a-zA-Z0-9_$ ) et sont générés de façon pseudo aléatoire à partir d'un générateur congruentiel linéaire à cycle complet, d'après une graine définie par l'utulusatuer, par composant.
la fonction hyperscript "h" peut être écrite telle quelle dans le code, ou à partir de JSX ou mieux encore à partir de chaînes de template Jade/PUG, car j'aime beaucoup ce format... ce sera l'ojbet d'un développement ultérieur, le temps d'étudier le fonctionnement de leurs lexer et parser.
La fonction "h" devra appartenir à une closure javascript, histoire de pouvoir redirger l'ensemble ses compositions d'appel, par composant et pour l'application sur le même pool de blocks.
Exemples:
oug2dom utilisera le lexer de PUG pour emballer les définitions de fonctions "h" dans le 2eme exemple pour obtenir ce qu'on voit dans le premier. Notez l'appel à memo() au début de chauqe exemple pour obtneir une fonction hyperscript "h" liée par closure à une partie du pool référencée par le hash de la chaîne 'cle-user-par composant.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 const h = meom('cle-user-par-composant') const view1 = ()data => h('div', { 'id': 'contanier'}, h('h1'), {}, 'my-awesome-vsom-lib'), h('ul', {}, h('li', { 'class': 'active'}, 'highlighted text !'), h('li', { }, 'awesome text' ), h('li', { }, 'another text )' ) ) // ou si on arrive a déternourner PUG/Jade const h = meom('cle-user-par-composant') // ES2015 tagged template string pug2memo `div#container h1 my-awesome-vdom-lib ul li (class="active") higligted text li awesome text another text `
Encore une fois je répète TOUT les appels à "h"' doivent se traduire par un stockage dans lamême structure de pool... A ce point je vais demander mles avis des uns et des autres sur la faisabilité d'un tel projet. N(hésitez pas à me poser des questions pour plus de détails.
Je vais préparer un dépôt pour stocker le résultat de mes tests en cas de demande. Mes récents posts sur le forum 'Général Javascript' se rapportent à ce projet.
salut, A + F - E
Partager