Un pequeño motor de plantilla Virtual DOM (512 byte) para proyectos integrados
| Es decir / borde | Firefox | Cromo | Safari | Ópera | iOS safari | Chrome para Android |
|---|---|---|---|---|---|---|
| Borde 14+ | 45+ | 49+ | 10+ | 37+ | 10.2+ | 55+ |
.dom toma prestados algunos conceptos de React.js (como los componentes reutilizables y el DOM virtual) e intenta replicarlos con la huella más pequeña posible, explotando las características de ES6 JavaScript.
¿Por qué? ¡Porque con tal biblioteca puede crear GUI poderosas en entornos de espacios ajustados, como los dispositivos IoT, donde guardar incluso un byte adicional realmente importa!
Pequeño por diseño : la biblioteca nunca debe exceder los 512 bytes en tamaño. El objetivo no es tener otro motor de plantilla, sino tener tantas características como sea posible en 512 bytes. Si se necesita una nueva característica, una otra debe ser sacraficada o el alcance debe reducirse.
Construido para el futuro : la biblioteca está explotando en gran medida las especificaciones de ES6, lo que significa que no es compatible con navegadores más antiguos. Actualmente está respaldado por el 90% de los navegadores en el mercado, pero espera que esto esté cerca del 100% en el próximo año.
Declarativo : Describa su HTML DOM de una manera estructurada y natural, ayudándole a crear interfaces de usuario potentes pero legibles.
Orientado a los componentes : al igual que React.js, .dom promueve el uso de componentes funcionales.
Aceleradores de "Escribir menos" : la API de la biblioteca está diseñada específicamente para tener nombres de funciones y aceleradores cortos, lo que le permite describir sus puntos de vista con menos código.
.dom ¿Estás usando .dom en tu proyecto? ¡Bifurca este repositorio y agregue el suyo en la lista!
Para una huella mínima, incluya dotdom.min.js.gz (512b) a su proyecto.
< script src =" dotdom.min.js.gz " />Alternativamente, puede incluir la versión minificada de la biblioteca directamente antes de su script. Simplemente copie pegar el código minificado.
Si ya conoce React.js, los siguientes ejemplos pueden ayudarlo a comprender cómo se relacionan las primitivas .dom que se relacionan con reaccionar.
Renderizar una estructura DOM muy simple.
| Reaccionar | .dom |
|---|---|
ReactDOM . render (
React . createElement ( 'div' , null , 'Hello world' ) ,
document . body
) ; | R (
H ( 'div' , 'Hello world' ) ,
document . body
) |
Creando un componente en el que puede pasar propiedades.
| Reaccionar | .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
) |
Creación de componentes que pueden mantener su propio estado.
| Reaccionar | .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
) |
El componente también puede suscribirse a eventos de ciclo de vida:
| Reaccionar | .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
) |
Las actualizaciones de Keyed es una característica de conciliación útil de React que permite que el motor de representación tome decisiones inteligentes sobre qué elementos actualizar.
Un caso particularmente útil es cuando está representando una lista dinámica de elementos. Dado que el motor de representación no entiende qué elemento ha cambiado, termina con actualizaciones incorrectas.
Para resolver este problema, los motores VDOM utilizan una propiedad key que identifica de manera única un elemento en el árbol. Sin embargo , eldom lo resuelve, manteniendo una copia del estado del elemento en la instancia del elemento VDOME en sí.
Esto significa que no necesita ninguna propiedad key , solo asegúrese de devolver la misma instancia de VDOM que antes.
Si está creando elementos dinámicos (por ejemplo, una matriz de elementos VDOM), .dom podría tener problemas para detectar el orden de actualización correcto.
| Reaccionar | .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
) |
Tenga en cuenta que la solución anterior actualizará correctamente los componentes con estado, incluso si su pedido ha cambiado. Sin embargo, si desea la funcionalidad completa y similar a React que actualiza las claves individuales, puede usar el complemento 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 ) ) ;
} Puede crear nodos VDOM CRUN (no conciliados) (por ejemplo, que llevan un contenido HTML arbitrario) estableciendo la propiedad .r de los ganchos objeto a cualquier valor de verdad.
Esto deshabilitará aún más la reconciliación con los nodos infantiles y, por lo tanto, mantendrá intacto su contenido.
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 )Rendera el árbol VNode dado al elemento DOM dado. Las actualizaciones adicionales de los componentes con estado solo ocurrirán en sus hijos inmediatos.
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" } )Crea un elemento vnode. Si se pasa una cadena como el primer argumento, creará un elemento HTML. Si se da una función, creará un componente con estado.
Las propiedades y los niños son opcionales y se pueden omitir.
En lugar de un nombre de etiqueta, puede proporcionar una función que devuelva un DOM virtual de acuerdo con alguna lógica de nivel superior. Dicha función tiene la siguiente firma:
const Component = ( props , state , setState , hooks ) {
// Return your Virtual DOM
return div ( ... )
} La propiedad props contiene el objeto Propiedades como se da cuando se creó el componente.
El state se inicializa a un objeto vacío {} y se actualiza llamando al método setState({ newState }) . Este último también activará una actualización del componente y sus hijos.
También puede asignar propiedades al objeto state directamente si no desea causar una actualización.
El objeto hooks se puede usar cuando desea registrar manejadores en los métodos de ciclo de vida del componente.
Similar a React, los componentes .dom tienen un ciclo de vida:
Para acceder a los métodos de ciclo de vida, debe usar el cuarto argumento en su función de componente. Más específicamente, debe impulsar su función de manejo en cualquiera de los siguientes campos:
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' ) Se puede extraer una función en taquigrafía como una propiedad de la función H Tales abreviados se comportan exactamente como H , pero con el nombre de la etiqueta ya poblado.
Se recomienda utilizar una tarea deconstructiva al comienzo de su script para ayudar a los minificadores de JavaScript a optimizar aún más el resultado:
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 ) En lugar de proporcionar el className como propiedad, puede usar la taquigrafía .className en combinación con los métodos de etiqueta de taquigrafía.
Esto es lo mismo que llamar div({className: 'className'}) y la interfaz de función es exactamente la misma que la anterior.
Nota: Puede agregar más de una clase concatenando más de una .class a la etiqueta. Por ejemplo: div.foo.bar es lo mismo que div({className: 'foo bar'}) .
Dado que el enfoque del proyecto es el pequeño tamaño, carece de controles de cordura. Esto lo hace susceptible a los errores. Tenga mucho cuidado con las siguientes advertencias:
No puede activar una actualización con una eliminación de propiedades. Debe establecer la nueva propiedad en un valor vacío en su lugar. Por ejemplo:
// Wrong
R ( div ( { className : 'foo' } ) , document . body ) ;
R ( div ( { } ) , document . body ) ;
// Correct
R ( div ( { className : 'foo' } ) , document . body ) ;
R ( div ( { className : '' } ) , document . body ) ; Nunca debe usar una propiedad llamada $ en sus componentes. Al hacerlo, hará que el objeto de la propiedad se considere como un nodo DOM virtual y conducirá a resultados inesperados.
// *NEVER* do this!
R ( H ( MyComponent , { $ : 'Foo' } ) , document . body ) K(state, components)En
plugin-keyed.min.js
Asegura que el estado de los componentes en la lista esté sincronizado, según su propiedad key . Esto le permite realizar actualizaciones con teclas similares a React como así:
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 ) )
} ¿Estás interesado en contribuir a .dom ? ¡Eres más que bienvenido! Solo asegúrese de seguir las pautas:
npm install
npm test && npm run build && ls -l dotdom.min.js.gz
Si pasan las pruebas y el tamaño de dotdom.min.js.gz es más pequeño o igual a 512 bytes, cree una solicitud de extracción. De lo contrario, reduzca su alcance o piense en otra implementación para recuperarla a 512 bytes.
Asegúrese de comenta correctamente su código, ya que probablemente tendrá que hacer una piratería de JavaScript extrema. Los Gudeliens son los siguientes:
/**
* 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 */
...Licenciado bajo la licencia Apache, versión 2.0