Un minuscule moteur de modèle DOM virtuel (512 octets) pour les projets intégrés
| C'est-à-dire / bord | Incendier | Chrome | Safari | Opéra | Safari iOS | Chrome pour Android |
|---|---|---|---|---|---|---|
| Edge 14+ | 45+ | 49+ | 10+ | 37+ | 10.2+ | 55+ |
.Dom emprunte certains concepts de React.js (tels que les composants réutilisables et le DOM virtuel) et essaie de les reproduire avec la plus petite empreinte possible, exploitant les fonctionnalités JavaScript ES6.
Pourquoi? Parce qu'avec une telle bibliothèque, vous pouvez créer de puissantes GUIS dans des environnements spatiaux serrés, tels que les appareils IoT, où la sauvegarde même un octet supplémentaire compte réellement!
Tiny by Design : La bibliothèque ne doit jamais dépasser les 512 octets. L'objectif n'est pas d'avoir un autre moteur de modèle, mais d'avoir autant de fonctionnalités que possible en 512 octets. Si une nouvelle fonctionnalité est nécessaire, une autre doit être sacrafiée ou si la portée doit être réduite.
Construit pour l'avenir : la bibliothèque exploite fortement les spécifications ES6, ce qui signifie qu'elle n'est pas prise en charge par les anciens navigateurs. Actuellement, il est soutenu par les 90% des navigateurs sur le marché, mais attendez-vous à ce que ce soit proche de 100% au cours de la prochaine année.
DÉCARRATIF : Décrivez votre DOM HTML de manière naturelle et naturelle, vous aidant à créer des interfaces utilisateur puissantes mais lisibles.
Composant orienté : tout comme react.js, .dom favorise l'utilisation de composants fonctionnels.
«Écrire moins» des accélérateurs : l'API de la bibliothèque est conçue spécifiquement pour avoir des noms de fonction et des accélérateurs courts, vous permettant de décrire vos vues avec moins de code.
.dom Utilisez-vous .dom dans votre projet? Fourk ce référentiel et ajoutez le vôtre sur la liste!
Pour une empreinte minimale, incluez dotdom.min.js.gz (512b) à votre projet.
< script src =" dotdom.min.js.gz " />Alternativement, vous pouvez simplement inclure la version minifiée de la bibliothèque directement avant votre script. Il suffit de copier-coller le code minifié.
Si vous connaissez déjà React.js, les exemples suivants peuvent vous aider à comprendre comment les primitives .dom sont liées à réagir.
Rendant une structure DOM très simple.
| Réagir | .dom |
|---|---|
ReactDOM . render (
React . createElement ( 'div' , null , 'Hello world' ) ,
document . body
) ; | R (
H ( 'div' , 'Hello world' ) ,
document . body
) |
Création d'un composant sur lequel vous pouvez passer les propriétés.
| Réagir | .dom |
|---|---|
function Hello ( props ) {
return React . createElement (
'div' , null , `Hello ${ props . toWhat } `
) ;
}
ReactDOM . render (
React . createElement (
Hello , { toWhat : 'World' } , null
) ,
document . body
) ; | function Hello ( props ) {
return H ( 'div' , `Hello ${ props . toWhat } ` ) ;
}
R (
H ( Hello , { toWhat : 'World' } ) ,
document . body
) |
Création de composants qui peuvent maintenir leur propre état.
| Réagir | .dom |
|---|---|
class Clickable extends React . Component {
constructor ( ) {
super ( ... arguments ) ;
this . state = {
clicks : 0
} ;
}
render ( ) {
const { clicks } = this . state ;
return React . createElement (
'button' , {
onClick ( ) {
this . setState ( { clicks : clicks + 1 } )
}
} , `Clicked ${ clicks } times`
) ;
}
}
ReactDOM . render (
React . createElement ( 'div' , null ,
React . createElement ( Clickable , null , null ) ,
React . createElement ( Clickable , null , null )
) ,
document . body
) ; | function Clickable ( props , state , setState ) {
const { clicks = 0 } = state ;
return H ( 'button' ,
{
onclick ( ) {
setState ( { clicks : clicks + 1 } )
}
} ,
`Clicked ${ clicks } times`
) ;
}
R (
H ( 'div' ,
H ( Clickable ) ,
H ( Clickable )
) ,
document . body
) |
Le composant peut également souscrire aux événements du cycle de vie:
| Réagir | .dom |
|---|---|
class WithLifeCycle extends React . Component {
constructor ( ) {
super ( ... arguments ) ;
this . state = {
mounted : "no"
} ;
}
componentDidMount ( ) {
this . setState ( { mounted : "yes" } )
}
render ( ) {
const { mounted } = this . state ;
return React . createElement (
'div' , null , `mounted = ${ mounted } `
) ;
}
}
ReactDOM . render (
React . createElement ( 'div' , null ,
React . createElement ( WithLifeCycle , null , null ) ,
) ,
document . body
) ; | function WithLifeCycle ( props , state , setState , hooks ) {
const { mounted = "no" } = state ;
hooks . m . push ( ( ) => {
setState ( { mounted : "yes" } )
} ) ;
return H ( 'div' ,
`mounted = ${ mounted } `
) ;
}
R (
H ( 'div' , H ( WithLifeCycle ) ) ,
document . body
) |
Les mises à jour à clé sont une fonction de réconciliation utile de React qui permet au moteur de rendu de prendre des décisions intelligentes sur les éléments à mettre à jour.
Un cas particulièrement utile est lorsque vous rendez une liste dynamique d'éléments. Étant donné que le moteur de rendu ne comprend pas quel élément a changé, il se termine par de mauvaises mises à jour.
Pour résoudre ce problème, les moteurs VDom utilisent une propriété key qui identifie de manière unique un élément de l'arbre. Cependant .dom le résout, en gardant une copie de l'état de l'élément dans l'instance d'élément VDom elle-même.
Cela signifie que vous n'avez pas besoin de propriété key , assurez-vous simplement de retourner la même instance VDom qu'auparavant.
Si vous créez des éléments dynamiques (par exemple, un tableau d'éléments VDom), .dom pourrait avoir du mal à détecter l'ordre de mise à jour correct.
| Réagir | .dom |
|---|---|
class Clickable extends React . Component {
constructor ( ) {
super ( ... arguments ) ;
this . state = {
clicks : 0
} ;
}
render ( ) {
const { clicks } = this . state ;
const { ket } = this . props ;
return React . createElement (
'button' , {
onClick ( ) {
this . setState ( { clicks : clicks + 1 } )
}
} , `clicks= ${ clicks } , key= ${ key } `
) ;
}
}
const list = [ "first" , "second" , "third" ] ;
const components = list . map ( key =>
React . createElement ( Clickable , { key } , null ) ;
ReactDOM . render (
React . createElement ( 'div' , null ,
components
) ,
document . body
) ; | function Clickable ( props , state , setState ) {
const { clicks = 0 } = state ;
const { key } = props ;
return H ( 'button' ,
{
onclick ( ) {
setState ( { clicks : clicks + 1 } )
}
} ,
`clicks= ${ clicks } , key= ${ key } `
) ;
}
const list = [ "first" , "second" , "third" ] ;
const components = list . map ( key =>
H ( Clickable , { key } ) ;
R (
H ( 'div' , components ) ,
document . body
) |
Notez que la solution ci-dessus mettra à jour correctement les composants avec état, même si leur commande a changé. Cependant, si vous voulez les fonctionnalités complètes et réagis qui mettent à jour les touches individuelles, vous pouvez utiliser le plug-in Keyed .
function Container ( props , state ) {
const { components } = props ;
// The function `K` accepts the component state and an array of components that
// contain the `key` property, and returns the same array of components, with their
// state correctly manipulated.
return H ( "div" , K ( state , components ) ) ;
} Vous pouvez créer des nœuds VDAM bruts (non réconciliés) (par exemple, qui portent un contenu HTML arbitraire) en définissant la propriété .r de l'objet Hooks à toute valeur de vérité.
Cela désactivera la réconciliation supplémentaire avec les nœuds enfants et maintiendra donc votre contenu intact.
function Description ( props , state , setState , hooks ) {
const { html } = props ;
hooks . r = 1 ; // Enable raw mode
return H ( 'div' , {
innerHTML : html
} )
} R( VNode, DOMElement ) R ( H ( 'div' , 'Hello' ) , document . body )Rend l'arbre VNode donné à l'élément DOM donné. D'autres mises à jour des composants avec état ne se produiront que sur leurs enfants immédiats.
H( tagName | function, [properties], [children ...]) H ( 'tag' )
H ( 'tag' , { prop : "value" } )
H ( 'tag' , H ( 'child' ) )
H ( 'tag' , { prop : "value" } , H ( 'child' ) )
H ( Component , { prop : "value" } )Crée un élément VNode. Si une chaîne est passée comme premier argument, elle créera un élément HTML. Si une fonction est donnée, elle créera un composant avec état.
Les propriétés et les enfants sont facultatifs et ils peuvent être omis.
Au lieu d'un nom de balise, vous pouvez fournir une fonction qui renvoie un Dom virtuel en fonction d'une logique de niveau supérieur. Une telle fonction a la signature suivante:
const Component = ( props , state , setState , hooks ) {
// Return your Virtual DOM
return div ( ... )
} La propriété props contient l'objet Propriétés comme indiqué lorsque le composant a été créé.
L' state est initialisé dans un objet vide {} et il est mis à jour en appelant la méthode setState({ newState }) . Ce dernier déclenchera également une mise à jour du composant et de ses enfants.
Vous pouvez également attribuer directement des propriétés à l'objet state si vous ne souhaitez pas provoquer de mise à jour.
L'objet hooks peut être utilisé lorsque vous souhaitez enregistrer les gestionnaires des méthodes de cycle de vie des composants.
Semblable à React, les composants .Dom ont un cycle de vie:
Pour accéder aux méthodes du cycle de vie, vous devez utiliser le quatrième argument sur votre fonction de composant. Plus précisément, vous devez pousser votre fonction de manutention dans l'un des champs suivants:
const Component = ( props , state , setState , hooks ) {
hooks . m . push ( ( domElement ) => {
// '.m' is called when the component is mounted
} ) ;
hooks . u . push ( ( ) => {
// `.u` is called when the component is unmounted
} ) ;
hooks . d . push ( ( domElement , previousDomElement ) => {
// `.d` is called when the component is updated
} ) ;
...
}tag( [properties], [children ...] ) const { div , span , a } = H ;
div ( 'hello' , span ( 'world' ) )
div ( 'click' , a ( { href : '#' } , 'Here' ) , 'to continue' ) Une fonction raccourci peut être extraite en tant que propriété de la fonction H Ces sténages se comportent exactement comme H , mais avec le nom de balise déjà peuplé.
Il est recommandé d'utiliser une affectation de déconstructrice au début de votre script afin d'aider les minifiers JavaScript optimiser davantage le résultat:
const {div, span, a, button} = H;
tag.class( [properties], [children ...] ) const { h1 , span , p } = H ;
h1 . short ( 'short header' , span . strong ( 'strong text' ) )
button . primary ( { onclick : handleClick } , 'Primary Action' )
p . bold . italic ( twitterPost ) Au lieu de fournir le className en tant que propriété, vous pouvez utiliser le raccourci .className en combinaison avec les méthodes de balise raccourci.
C'est la même chose que l'appel div({className: 'className'}) et l'interface de fonction est exactement la même que celle ci-dessus.
Remarque: vous pouvez ajouter plus d'une classe en concaténant plus d'un .class à la balise. Par exemple: div.foo.bar est le même que div({className: 'foo bar'}) .
Étant donné que l'objectif du projet est la petite taille, il manque de contrôles de santé mentale. Cela le rend sensible aux erreurs. Soyez très prudent avec les mises en garde suivantes:
Vous ne pouvez pas déclencher une mise à jour avec une suppression de propriétés. Vous devez plutôt définir la nouvelle propriété sur une valeur vide. Par exemple:
// Wrong
R ( div ( { className : 'foo' } ) , document . body ) ;
R ( div ( { } ) , document . body ) ;
// Correct
R ( div ( { className : 'foo' } ) , document . body ) ;
R ( div ( { className : '' } ) , document . body ) ; Vous ne devez jamais utiliser une propriété nommée $ dans vos composants. Cela fera que l'objet de propriété soit considéré comme un nœud DOM virtuel et conduira à des résultats inattendus.
// *NEVER* do this!
R ( H ( MyComponent , { $ : 'Foo' } ) , document . body ) K(state, components)Dans
plugin-keyed.min.js
S'assure que l'état des composants de la liste est synchronisé, selon leur propriété key . Cela vous permet de faire des mises à jour à clé de type React comme ainsi:
function ValueRenderer ( ... ) {
...
}
function MyComponent ( props , state ) {
const { values } = props ;
const components = values . map ( value => {
H ( ValueRenderer , {
key : value ,
value : value
} ) ;
} )
// Synchronize state of components, based on their key
return H ( 'div' , K ( state , components ) )
} Êtes-vous intéressé à contribuer à .dom ? Vous êtes plus que bienvenu! Assurez-vous simplement de suivre les directives:
npm install
npm test && npm run build && ls -l dotdom.min.js.gz
Si les tests passent et la taille de dotdom.min.js.gz est plus petit ou égal à 512 octets, créez une demande de traction. Sinon, réduisez votre portée ou pensez à une autre implémentation afin de le ramener à 512 octets.
Assurez-vous de commettre correctement votre code, car vous devrez très probablement faire un piratage JavaScript extrême. Les Gudeliens sont les suivants:
/**
* Functions are commented as JSDoc blocks
*
* @param {VNode|Array<VNode>} vnodes - The node on an array of nodes to render
* ...
*/
global . R = render = (
vnodes , // Flat-code comments start on column 70 and
dom , // wrap after column 120.
/* Logical separations can be commented like this */
...Licencié sous la licence Apache, version 2.0