Una biblioteca DOM virtual con un enfoque en la simplicidad, la modularidad, las características potentes y el rendimiento.
Gracias a Browserstack por proporcionar acceso a sus excelentes herramientas de prueba de navegador.
Inglés | 简体中文 | hindi
Virtual DOM es increíble. Nos permite expresar la visión de nuestra aplicación en función de su estado. Pero las soluciones existentes estaban demasiado hinchadas, demasiado lentas, carecían de características, tenían una API sesgada hacia OOP y/o carecían de características que necesitaba.
Snabbdom consiste en un núcleo extremadamente simple, performador y extensible que es solo ≈ 200 SLOC. Ofrece una arquitectura modular con una rica funcionalidad para extensiones a través de módulos personalizados. Para mantener el núcleo simple, toda la funcionalidad no esencial se delega a los módulos.
¡Puedes moldear Snabbdom en lo que desees! Elija, elija y personalice la funcionalidad que desee. Alternativamente, puede usar las extensiones predeterminadas y obtener una biblioteca DOM virtual con alto rendimiento, tamaño pequeño y todas las características que se enumeran a continuación.
h Función para crear fácilmente nodos DOM virtuales.h helper. import {
init ,
classModule ,
propsModule ,
styleModule ,
eventListenersModule ,
h
} from "snabbdom" ;
const patch = init ( [
// Init patch function with chosen modules
classModule , // makes it easy to toggle classes
propsModule , // for setting properties on DOM elements
styleModule , // handles styling on elements with support for animations
eventListenersModule // attaches event listeners
] ) ;
const container = document . getElementById ( "container" ) ;
const vnode = h (
"div#container.two.classes" ,
{ on : { click : ( ) => console . log ( "div clicked" ) } } ,
[
h ( "span" , { style : { fontWeight : "bold" } } , "This is bold" ) ,
" and this is just normal text" ,
h ( "a" , { props : { href : "/foo" } } , "I'll take you places!" )
]
) ;
// Patch into empty DOM element – this modifies the DOM as a side effect
patch ( container , vnode ) ;
const newVnode = h (
"div#container.two.classes" ,
{ on : { click : ( ) => console . log ( "updated div clicked" ) } } ,
[
h (
"span" ,
{ style : { fontWeight : "normal" , fontStyle : "italic" } } ,
"This is now italic type"
) ,
" and this is still just normal text" ,
h ( "a" , { props : { href : "/bar" } } , "I'll take you places!" )
]
) ;
// Second `patch` invocation
patch ( vnode , newVnode ) ; // Snabbdom efficiently updates the old view to the new state initpatchhfragment (experimental)toVNodeinitinsertremovedestroyremovedestroyEl núcleo de Snabbdom proporciona solo la funcionalidad más esencial. Está diseñado para ser lo más simple posible mientras es rápido y extensible.
init El núcleo expone solo una sola función init . Este init toma una lista de módulos y devuelve una función patch que usa el conjunto especificado de módulos.
import { classModule , styleModule } from "snabbdom" ;
const patch = init ( [ classModule , styleModule ] ) ;patch La función patch devuelta por init toma dos argumentos. El primero es un elemento DOM o un VNODE que representa la vista actual. El segundo es un VNode que representa la nueva vista actualizada.
Si se pasa un elemento DOM con un padre, newVnode se convertirá en un nodo DOM, y el elemento aprobado será reemplazado por el nodo DOM creado. Si se pasa un VNode antiguo, Snabbdom lo modificará de manera eficiente para que coincida con la descripción en el nuevo VNode.
Cualquier antiguo VNode aprobado debe ser el VNode resultante de una llamada anterior a patch . Esto es necesario ya que Snabbdom almacena información en el VNode. Esto hace posible implementar una arquitectura más simple y más representante. Esto también evita la creación de un nuevo árbol VNode antiguo.
patch ( oldVnode , newVnode ) ; Si bien no hay API específicamente para eliminar un árbol VNode de su elemento de punto de montaje, una forma de casi lograr esto es proporcionar un comentario VNode como el segundo argumento para patch , como:
patch (
oldVnode ,
h ( "!" , {
hooks : {
post : ( ) => {
/* patch complete */
}
}
} )
) ;Por supuesto, entonces todavía hay un solo nodo de comentarios en el punto de montaje.
h Se recomienda que use h para crear vnodes. Acepta una etiqueta/selector como una cadena, un objeto de datos opcional y una cadena opcional o una matriz de niños.
import { h } from "snabbdom" ;
const vnode = h ( "div#container" , { style : { color : "#000" } } , [
h ( "h1.primary-title" , "Headline" ) ,
h ( "p" , "A paragraph" )
] ) ;fragment (experimental)PRECAUCIÓN: Esta característica es actualmente experimental y debe optarse. Su API puede cambiarse sin un aumento de la versión principal.
const patch = init ( modules , undefined , {
experimental : {
fragments : true
}
} ) ;Crea un nodo virtual que se convertirá en un fragmento de documento que contiene a los hijos dados.
import { fragment , h } from "snabbdom" ;
const vnode = fragment ( [ "I am" , h ( "span" , [ " a" , " fragment" ] ) ] ) ;toVNodeConvierte un nodo DOM en un nodo virtual. Especialmente bueno para parchear sobre contenido HTML generado por el lado del servidor.
import {
init ,
styleModule ,
attributesModule ,
h ,
toVNode
} from "snabbdom" ;
const patch = init ( [
// Initialize a `patch` function with the modules used by `toVNode`
attributesModule // handles attributes from the DOM node
datasetModule , // handles `data-*` attributes from the DOM node
] ) ;
const newVNode = h ( "div" , { style : { color : "#000" } } , [
h ( "h1" , "Headline" ) ,
h ( "p" , "A paragraph" ) ,
h ( "img" , { attrs : { src : "sunrise.png" , alt : "morning sunrise" } } )
] ) ;
patch ( toVNode ( document . querySelector ( ".container" ) ) , newVNode ) ;Los ganchos son una forma de conectarse al ciclo de vida de los nodos DOM. Snabbdom ofrece una rica selección de ganchos. Los ganchos se usan tanto por módulos para extender Snabbdom como en código normal para ejecutar el código arbitrario en los puntos deseados en la vida de un nodo virtual.
| Nombre | Activado cuando | Argumentos a la devolución de llamada |
|---|---|---|
pre | El proceso de parche comienza | ninguno |
init | Se ha agregado un vnode | vnode |
create | Se ha creado un elemento DOM basado en un VNode | emptyVnode, vnode |
insert | Se ha insertado un elemento en el DOM | vnode |
prepatch | un elemento está a punto de ser parcheado | oldVnode, vnode |
update | Se está actualizando un elemento | oldVnode, vnode |
postpatch | Un elemento ha sido parcheado | oldVnode, vnode |
destroy | un elemento se está eliminando directa o indirectamente | vnode |
remove | Se está eliminando directamente un elemento del DOM | vnode, removeCallback |
post | El proceso de parche se realiza | ninguno |
Los siguientes ganchos están disponibles para módulos: pre , create , update , destroy , remove , post .
Los siguientes ganchos están disponibles en la propiedad de hook de elementos individuales: init , create , insert , prepatch , update , postpatch , destroy , remove .
Para usar ganchos, pasarlos como un objeto para el campo hook del argumento del objeto de datos.
h ( "div.row" , {
key : movie . rank ,
hook : {
insert : ( vnode ) => {
movie . elmHeight = vnode . elm . offsetHeight ;
}
}
} ) ; initEste gancho se invoca durante el proceso de parche cuando se ha encontrado un nuevo nodo virtual. El gancho se llama antes de que Snabbdom haya procesado el nodo de cualquier manera. Es decir, antes de que haya creado un nodo DOM basado en el VNode.
insertEste gancho se invoca una vez que el elemento DOM para un VNode se ha insertado en el documento y el resto del ciclo del parche se realiza. Esto significa que puede hacer mediciones DOM (como usar GetBoundingClientRect en este gancho de manera segura, sabiendo que no se cambiarán elementos que puedan afectar la posición de los elementos insertados.
remove Le permite conectarse a la eliminación de un elemento. El gancho se llama una vez que se eliminará un VNode del DOM. La función de manejo recibe tanto el VNODE como una devolución de llamada. Puede controlar y retrasar la eliminación con la devolución de llamada. La devolución de llamada debe invocarse una vez que el gancho haya realizado su negocio, y el elemento solo se eliminará una vez que todos los ganchos remove hayan invocado su devolución de llamada.
El gancho solo se activa cuando un elemento se debe eliminar de su padre, no si es el hijo de un elemento que se elimina. Para eso, vea el Hook destroy .
destroyEste gancho se invoca en un nodo virtual cuando su elemento DOM se elimina del DOM o si su padre se elimina del DOM.
Para ver la diferencia entre este gancho y el gancho remove , considere un ejemplo.
const vnode1 = h ( "div" , [ h ( "div" , [ h ( "span" , "Hello" ) ] ) ] ) ;
const vnode2 = h ( "div" , [ ] ) ;
patch ( container , vnode1 ) ;
patch ( vnode1 , vnode2 ) ; Aquí destroy se desencadena tanto para el elemento div interno como para el elemento span que contiene. remove , por otro lado, solo se activa en el elemento div porque es el único elemento que se separa de su padre.
Puede, por ejemplo, usar remove para activar una animación cuando se elimina un elemento y usar el gancho destroy para animar adicionalmente la desaparición de los hijos del elemento eliminado.
Los módulos funcionan registrando oyentes globales para ganchos. Un módulo es simplemente un diccionario de mapeo de nombres de gancho a las funciones.
const myModule = {
create : ( oldVnode , vnode ) => {
// invoked whenever a new virtual node is created
} ,
update : ( oldVnode , vnode ) => {
// invoked whenever a virtual node is updated
}
} ;Con este mecanismo, puede aumentar fácilmente el comportamiento de Snabbdom. Para la demostración, eche un vistazo a las implementaciones de los módulos predeterminados.
Esto describe los módulos centrales. Todos los módulos son opcionales. Ejemplos JSX suponen que está utilizando el jsx Pragma proporcionado por esta biblioteca.
El módulo de clase proporciona una manera fácil de alternar dinámicamente las clases en los elementos. Espera un objeto en la propiedad de datos class . El objeto debe asignar nombres de clase a los booleanos que indicen si la clase debe permanecer o no ir al VNode.
h ( "a" , { class : { active : true , selected : false } } , "Toggle" ) ; En JSX, puedes usar class como esta:
< div class = { { foo : true , bar : true } } />
// Renders as: <div class="foo bar"></div>Le permite establecer propiedades en los elementos DOM.
h ( "a" , { props : { href : "/foo" } } , "Go to Foo" ) ; En JSX, puedes usar props como este:
< input props = { { name : "foo" } } />
// Renders as: <input name="foo" /> with input.name === "foo"Las propiedades solo se pueden configurar. No eliminado. A pesar de que los navegadores permiten la adición y eliminación de propiedades personalizadas, este módulo no intentará la eliminación. Esto tiene sentido, porque las propiedades DOM nativas no se pueden eliminar. Y si está utilizando propiedades personalizadas para almacenar valores o hacer referencia a objetos en el DOM, considere usar los atributos de datos-* en su lugar. Quizás a través del módulo de conjunto de datos.
Igual que los accesorios, pero establece atributos en lugar de propiedades en elementos DOM.
h ( "a" , { attrs : { href : "/foo" } } , "Go to Foo" ) ; En JSX, puedes usar attrs como este:
< div attrs = { { "aria-label" : "I'm a div" } } />
// Renders as: <div aria-label="I'm a div"></div> Los atributos se agregan y se actualizan usando setAttribute . En el caso de un atributo que se había agregado/establecido previamente y ya no está presente en el objeto attrs , se elimina de la lista de atributos del elemento DOM utilizando removeAttribute .
En el caso de los atributos booleanos (por ejemplo, disabled , hidden , selected ...), el significado no depende del valor del atributo ( true o false ), sino que depende en cambio de la presencia/ausencia del atributo en el elemento DOM. Esos atributos se manejan de manera diferente por el módulo: si un atributo booleano se establece en un valor falsado ( 0 , -0 , null , false , NaN , undefined o la cadena vacía ( "" )), entonces el atributo se eliminará de la lista de atributos del elemento DOM.
Le permite establecer atributos de datos personalizados ( data-* ) en los elementos DOM. Luego se puede acceder a estos con la propiedad htmlelement.dataSet.
h ( "button" , { dataset : { action : "reset" } } , "Reset" ) ; En JSX, puede usar dataset como este:
< div dataset = { { foo : "bar" } } />
// Renders as: <div data-foo="bar"></div>El módulo de estilo es para hacer que su HTML se vea resbaladizo y animado sin problemas. En esencia, le permite establecer propiedades CSS en elementos.
h (
"span" ,
{
style : {
border : "1px solid #bada55" ,
color : "#c0ffee" ,
fontWeight : "bold"
}
} ,
"Say my name, and every colour illuminates"
) ; En JSX, puedes usar style como este:
< div
style = { {
border : "1px solid #bada55" ,
color : "#c0ffee" ,
fontWeight : "bold"
} }
/>
// Renders as: <div style="border: 1px solid #bada55; color: #c0ffee; font-weight: bold"></div> Se admiten propiedades personalizadas de CSS (también conocidas como variables CSS), deben tener prefijo --
h (
"div" ,
{
style : { "--warnColor" : "yellow" }
} ,
"Warning"
) ; Puede especificar que las propiedades se retrasen. Cada vez que cambian estas propiedades, el cambio no se aplica hasta después del siguiente cuadro.
h (
"span" ,
{
style : {
opacity : "0" ,
transition : "opacity 1s" ,
delayed : { opacity : "1" }
}
} ,
"Imma fade right in!"
) ;Esto hace que sea fácil animar declarativamente la entrada de elementos.
El all valor de transition-property no es compatible.
remove Los estilos establecidos en la propiedad remove entrarán en vigencia una vez que el elemento esté a punto de eliminarse del DOM. Los estilos aplicados deben animarse con transiciones CSS. Solo una vez que todos los estilos sean animados, el elemento se eliminará del DOM.
h (
"span" ,
{
style : {
opacity : "1" ,
transition : "opacity 1s" ,
remove : { opacity : "0" }
}
} ,
"It's better to fade out than to burn away"
) ;Esto hace que sea fácil animar declarativamente la eliminación de elementos.
El all valor de transition-property no es compatible.
destroy h (
"span" ,
{
style : {
opacity : "1" ,
transition : "opacity 1s" ,
destroy : { opacity : "0" }
}
} ,
"It's better to fade out than to burn away"
) ; El all valor de transition-property no es compatible.
El módulo de oyentes de eventos ofrece capacidades potentes para adjuntar a los oyentes de eventos.
Puede adjuntar una función a un evento en un VNode suministrando un objeto en on con una propiedad correspondiente al nombre del evento que desea escuchar. La función se llamará cuando ocurra el evento y se pasará al objeto del evento que le pertenece.
function clickHandler ( ev ) {
console . log ( "got clicked" ) ;
}
h ( "div" , { on : { click : clickHandler } } ) ; En JSX, puedes usar on :
< div on = { { click : clickHandler } } />Snabbdom permite intercambiar controladores de eventos entre renders. Esto sucede sin tocar los manejadores de eventos unidos al DOM.
Sin embargo, tenga en cuenta que debe tener cuidado al compartir los manejadores de eventos entre VNodes , debido a la técnica que este módulo utiliza para evitar la reinicia de los controladores de eventos al DOM. (Y en general, no se garantiza que compartir datos entre VNodes funcione, porque los módulos pueden mutar los datos dados).
En particular, no debes hacer algo como esto:
// Does not work
const sharedHandler = {
change : ( e ) => {
console . log ( "you chose: " + e . target . value ) ;
}
} ;
h ( "div" , [
h ( "input" , {
props : { type : "radio" , name : "test" , value : "0" } ,
on : sharedHandler
} ) ,
h ( "input" , {
props : { type : "radio" , name : "test" , value : "1" } ,
on : sharedHandler
} ) ,
h ( "input" , {
props : { type : "radio" , name : "test" , value : "2" } ,
on : sharedHandler
} )
] ) ; Para muchos de estos casos, puede usar controladores basados en matriz en su lugar (descrito anteriormente). Alternativamente, simplemente asegúrese de que cada nodo se pase único on los valores:
// Works
const sharedHandler = ( e ) => {
console . log ( "you chose: " + e . target . value ) ;
} ;
h ( "div" , [
h ( "input" , {
props : { type : "radio" , name : "test" , value : "0" } ,
on : { change : sharedHandler }
} ) ,
h ( "input" , {
props : { type : "radio" , name : "test" , value : "1" } ,
on : { change : sharedHandler }
} ) ,
h ( "input" , {
props : { type : "radio" , name : "test" , value : "2" } ,
on : { change : sharedHandler }
} )
] ) ; SVG simplemente funciona cuando se usa la función h para crear nodos virtuales. Los elementos SVG se crean automáticamente con los espacios de nombres apropiados.
const vnode = h ( "div" , [
h ( "svg" , { attrs : { width : 100 , height : 100 } } , [
h ( "circle" , {
attrs : {
cx : 50 ,
cy : 50 ,
r : 40 ,
stroke : "green" ,
"stroke-width" : 4 ,
fill : "yellow"
}
} )
] )
] ) ;Consulte también el ejemplo de SVG y el ejemplo de carrusel SVG.
Ciertos navegadores (como IE <= 11) no admiten la propiedad classList en elementos SVG. Debido a que el módulo de clase usa internamente classList , no funcionará en este caso a menos que use un polyfill de lista de clases. (Si no desea usar un polyfill, puede usar el atributo class con el módulo de atributos ).
La función thunk toma un selector, una clave para identificar un Thunk, una función que devuelve un VNODE y una cantidad variable de parámetros de estado. Si se invoca, la función de renderización recibirá los argumentos estatales.
thunk(selector, key, renderFn, [stateArguments])
El renderFn se invoca solo si se cambia el renderFn o la longitud de la matriz [stateArguments] o sus elementos se cambian.
La key es opcional. Debe suministrarse cuando el selector no es único entre los hermanos Thunks. Esto asegura que el thunk siempre coincida correctamente cuando se diferencia.
Los Thunks son una estrategia de optimización que se puede usar cuando se trata de datos inmutables.
Considere una función simple para crear un nodo virtual basado en un número.
function numberView ( n ) {
return h ( "div" , "Number is: " + n ) ;
} La vista depende solo de n . Esto significa que si n no cambia, entonces crear el nodo DOM virtual y parcharlo contra el VNODE antiguo es un desperdicio. Para evitar la sobrecarga, podemos usar la función thunk Helper.
function render ( state ) {
return thunk ( "num" , numberView , [ state . number ] ) ;
} En lugar de invocar la función numberView , esto solo colocará un VNODE ficticio en el árbol virtual. Cuando Snabbdom parche este vnode ficticio contra un VNode anterior, comparará el valor de n . Si n no cambia, simplemente reutilizará el viejo VNode. Esto evita recrear la vista de números y el proceso DIFF por completo.
La función de vista aquí es solo un ejemplo. En la práctica, los Thunks solo son relevantes si está representando una visión complicada que lleva un tiempo computacional significativo para generar.
Tenga en cuenta que los fragmentos JSX aún son experimentales y deben optarse. Consulte la sección fragment para más detalles.
Agregue las siguientes opciones a su tsconfig.json :
{
"compilerOptions" : {
"jsx" : " react " ,
"jsxFactory" : " jsx " ,
"jsxFragmentFactory" : " Fragment "
}
} Luego, asegúrese de usar la extensión del archivo .tsx e importe la función jsx y la función Fragment en la parte superior del archivo:
import { Fragment , jsx , VNode } from "snabbdom" ;
const node : VNode = (
< div >
< span > I was created with JSX </ span >
</ div >
) ;
const fragment : VNode = (
< >
< span > JSX fragments </ span >
are experimentally supported
</ >
) ;Agregue las siguientes opciones a su configuración de Babel:
{
"plugins" : [
[
" @babel/plugin-transform-react-jsx " ,
{
"pragma" : " jsx " ,
"pragmaFrag" : " Fragment "
}
]
]
} Luego importe la función jsx y la función Fragment en la parte superior del archivo:
import { Fragment , jsx } from "snabbdom" ;
const node = (
< div >
< span > I was created with JSX </ span >
</ div >
) ;
const fragment = (
< >
< span > JSX fragments </ span >
are experimentally supported
</ >
) ; Propiedades
La propiedad sel especifica el elemento HTML del VNode, opcionalmente su id prefijada por un # , y cero o más clases cada una prefijadas por a . . La sintaxis está inspirada en selectores CSS. Aquí hay algunos ejemplos:
div#container.bar.baz : un elemento div con el container de identificación y las clases bar y baz .li : un elemento li sin id ni clases.button.alert.primary - elemento button con las dos clases alert y primary . El selector está destinado a ser estático , es decir, no debe cambiar durante la vida del elemento. Para establecer una id dinámica, use el módulo Props y para establecer clases dinámicas use el módulo de clase.
Dado que el selector es estático, Snabbdom lo usa como parte de una identidad vnodes. Por ejemplo, si los dos niños vnodes
[ h ( "div#container.padding" , children1 ) , h ( "div.padding" , children2 ) ] ;están parcheados contra
[ h ( "div#container.padding" , children2 ) , h ( "div.padding" , children1 ) ] ;Luego, Snabbdom usa el selector para identificar los VNodes y reordenarlos en el árbol DOM en lugar de crear un nuevo elemento DOM. Este uso de selectores evita la necesidad de especificar claves en muchos casos.
La propiedad .data de un nodo virtual es el lugar para agregar información para que los módulos accedan y manipulen el elemento DOM real cuando se crea; Agregue estilos, clases CSS, atributos, etc.
El objeto de datos es el segundo parámetro (opcional) a h()
Por ejemplo h('div', {props: {className: 'container'}}, [...]) producirá un nodo virtual con
( {
props : {
className : "container"
}
} ) ; como su objeto .data .
La propiedad .children de un nodo virtual es el tercer parámetro (opcional) a h() durante la creación. .children son simplemente una variedad de nodos virtuales que deben agregarse como hijos del nodo DOM principal tras la creación.
Por ejemplo h('div', {}, [ h('h1', {}, 'Hello, World') ]) creará un nodo virtual con
[
{
sel : "h1" ,
data : { } ,
children : undefined ,
text : "Hello, World" ,
elm : Element ,
key : undefined
}
] ; como su propiedad. .children .
La propiedad .text se crea cuando se crea un nodo virtual con un solo niño que posee texto y solo requiere document.createTextNode() para ser utilizado.
Por ejemplo: h('h1', {}, 'Hello') creará un nodo virtual con Hello como su propiedad .text .
La propiedad .elm de un nodo virtual es un puntero al nodo DOM real creado por Snabbdom. Esta propiedad es muy útil para hacer cálculos en ganchos y módulos.
La propiedad .key se crea cuando se proporciona una clave dentro de su objeto .data . La propiedad .key se usa para mantener punteros a los nodos DOM que existían previamente para evitar recrearlos si es innecesario. Esto es muy útil para cosas como la reordenamiento de la lista. Una clave debe ser una cadena o un número para permitir la búsqueda adecuada, ya que se almacena internamente como un par de clave/valor dentro de un objeto, donde .key Key es la clave y el valor es la propiedad .elm creada.
Si se proporciona, la propiedad .key debe ser única entre los elementos hermanos.
Por ejemplo: h('div', {key: 1}, []) creará un objeto de nodo virtual con una propiedad .key con el valor de 1 .
Snabbdom es una biblioteca DOM virtual de bajo nivel. No se opina con respecto a cómo debe estructurar su aplicación.
Aquí hay algunos enfoques para construir aplicaciones con Snabbdom.
Asegúrese de compartirlo si está construyendo una aplicación de otra manera usando Snabbdom.
Los paquetes relacionados con Snabbdom deben etiquetarse con la palabra clave snabbdom y publicarse en NPM. Se pueden encontrar usando las keywords:snabbdom .
Uncaught NotFoundError: Failed to execute 'insertBefore' on 'Node':
The node before which the new node is to be inserted is not a child of this node.
La razón de este error es la reutilización de VNodes entre parches (ver ejemplo de código), Snabbdom almacena nodos DOM reales dentro de los nodos DOM virtuales que se les pasa como una mejora del rendimiento, por lo que no se admite reutilizar nodos entre parches.
const sharedNode = h ( "div" , { } , "Selected" ) ;
const vnode1 = h ( "div" , [
h ( "div" , { } , [ "One" ] ) ,
h ( "div" , { } , [ "Two" ] ) ,
h ( "div" , { } , [ sharedNode ] )
] ) ;
const vnode2 = h ( "div" , [
h ( "div" , { } , [ "One" ] ) ,
h ( "div" , { } , [ sharedNode ] ) ,
h ( "div" , { } , [ "Three" ] )
] ) ;
patch ( container , vnode1 ) ;
patch ( vnode1 , vnode2 ) ;Puede solucionar este problema creando una copia poco profunda del objeto (aquí con sintaxis de dispersión del objeto):
const vnode2 = h ( "div" , [
h ( "div" , { } , [ "One" ] ) ,
h ( "div" , { } , [ { ... sharedNode } ] ) ,
h ( "div" , { } , [ "Three" ] )
] ) ;Otra solución sería envolver vnodes compartidos en una función de fábrica:
const sharedNode = ( ) => h ( "div" , { } , "Selected" ) ;
const vnode1 = h ( "div" , [
h ( "div" , { } , [ "One" ] ) ,
h ( "div" , { } , [ "Two" ] ) ,
h ( "div" , { } , [ sharedNode ( ) ] )
] ) ; Las solicitudes de extracción de que la comunidad pueda cuidar de proporcionar retroalimentación debe fusionarse después de que se brinde tal oportunidad de unos pocos días.