编程知识 cdmana.com

Algorithme diff et réalisation simple du Dom virtuel

Un.、VirtuelDOMRéalisation

VirtueldomTout le processus:

  1. Créer un virtueldomStructure des données pour
  2. Générer un virtueldom
  3. Montage virtueldom
  4. Si l'élément parent n'est pas virtueldom,Appelez directementmountMéthode de montage
  5. Si l'élément parent a un virtueldom,Et doit être mis à jour,Appelle justepatchMise à jour de la méthode
  6. patchDans la méthode pour les anciens et les nouveauxVNodeDetype,props,Propriétés à mettre à jour
  7. Ensuite, il y a l'ancien et le nouveauVNodeDechildrenMise à jour
  8. Si l'ancienVNodeDechildrenEt nouveauVNodeDechildrenTout existe.,Il faut l'utiliser.diffAlgorithme en coursnodeTreeMise à jour de

1. Définir le virtuelDOMStructure des données pour

// h.js
// VNodeNoeud
const createVNode = (type, props, key, $$) => {  
     return {   
         type, // div / CompoentA / ''(Texte)  
         props, // Propriétés etchildren  
         key,  
         $$ // Fonctions internes
     }}
// textNoeud
const createText = (text) => {  
    return {   
         type: '',   
         props: {    
              nodeValue: text + ''   
         },   
         $$: { 
              flag: NODE_FLAG.TEXT 
         }  
    }
}
//  Utiliser le budget BIT pour déterminer le type de noeud 
export const NODE_FLAG = { 
   EL: 1, // Élément element 
   TEXT: 1 << 1}
Copier le Code

2. Définir la génération virtuelle DOMMéthode objet pour

// h.js
// h('div', { className: 'padding20'}, 'hello world!') 
export const h = (type, props, ...kids) => {  
          props = props || {}  
          let key = props.key || void 0 
          kids = normalize(props.children || kids) 
          if (kids.length) props.children = kids.length === 1 ? kids[0] : kids 
          const $$ = {}  
          $$.el = null  
          $$.flag = type === '' ? NODE_FLAG.TEXT : NODE_FLAG.EL 
          return createVNode(type, props, key, $$)}
Copier le Code

Exemple:

import { h } from './h.js'
const vnode = h(  'ul',  {   
                  style: {      
                       width: '100px',   
                       height: '100px',    
                       backgroundColor: 'green'   
                  }  
                }, 
                [    
                    h('li', { key: 'li-a' }, 'this is li a'), 
                    h('li', { key: 'li-b' }, 'this is li b'), 
                    h('li', { key: 'li-c' }, 'this is li c'),   
                     h('li', { key: 'li-d' }, 'this is li d'), 
                ])
console.log(vnode)
Copier le Code

3. Rendre

//render.js
import { mount } from './mount.js'
import { patch } from './patch.js'
export const render = (vnode, parent) => { 
                         let prev = parent._vnode 
                         //  Le noeud parent n'a pas de virtuel dom, Montage direct  
                         if (!prev) {  
                                // mount La méthode est décrite dans la Section4Point  
                                mount(vnode, parent)   
                                parent._vnode = vnode 
                         }  else {   
                             if (vnode) {
                                    // Les anciens et les nouveaux vnodeTree Tout existe.,En courspatch  
                                    // patchLa méthode est décrite dans la Section6Point
                                    patch(prev, vnode, parent)   
                                    parent._vnode = vnode  
                              }  else {  
                                   //  Il n'y a pas de nouveau  vnodeTree,Directremove
                                    parent.removeChild(prev.$$.el)  
                              }  
                          }
                   }
Copier le Code

4. Opération de montage mount

// mount.js
import { NODE_FLAG } from './h.js'
import { patchProps } from './patch.js'
export const mount = (vnode, parent, refNode) => {
      if (!parent) throw new Error(' Veuillez passer dans l'élément parent ')  
      const $$ = vnode.$$  
      //  Pour le noeud de texte 
      if ($$.flag & NODE_FLAG.TEXT) {  
          const el = document.createTextNode(vnode.props.nodeValue)   
          vnode.el = el  
          //  Rendre le noeud de texte  
          parent.appendChild(el) 
      }  else if ($$.flag & NODE_FLAG.EL) { // Noeud d'élément Ne pense pas. type  C'est le cas d'un composant    
          const { type, props } = vnode   
          const el = document.createElement(type)   
          vnode.el = el  
          const { children, ...rest } = props    
          if (Object.keys(rest).length) {   
               for (let key of Object.keys(rest)) {
                      //  Traverser sauf childrenAttributs autres que,
                      // Vas - y.patch propsLes propriétés à l'intérieur, Par exempleclass,styleAttendez.
                      // patchPropsLa méthode est décrite dans la Section5Point
                      patchProps(key, null, rest[key], el)     
                }    
          }   
          if (children) {    
               const __children = Array.isArray(children) ? children : [children]  
               for (let child of __children) {  
                      // Si elle existechldren,  Ils sont montés VNode Sur l'élément correspondant   
                      mount(child, el)   
               }   
          } 
          //  Emplacement du rendu 
          refNode ? parent.insertBefore(el, refNode) : parent.appendChild(el)  }}
Copier le Code

5.patchProps Méthodes

// patch.js
export const patchProps = (key, prev, next, el) => { 
         // style  
         if (key === 'style') {  
            // { style: { margin: '0px', padding: '10px' }}  
            if (next)  {   
               for (let k in next) {      
                  el.style[k] = next[k]     
               }    
            }
            // { style: { padding: '0px', color: 'red' } }   
            if (prev)  {   
                 for (let k in prev) {    
                     if (!next.hasOwnProperty(k)) {      
                          el.style[k] = ''    
                      }   
                 } 
            }
         }  
         // class  
         else if (key === 'className') {  
                if (!el.classList.contains(next)) {     
                       el.classList.add(next)  
                  }  
          }  
          // events 
          else if (key[0] === 'o' && key[1] === 'n') {  
                prev && el.removeEventListener(key.slice(2).toLowerCase(), prev)  
                next && el.addEventListener(key.slice(2).toLowerCase(), next)  
          } 
          else if (/\[A-Z]|^(?:value|checked|selected|muted)$/.test(key)) {  
                el[key] = next 
          } 
          else {    
            el.setAttribute && el.setAttribute(key, next)  
          }
}
Copier le Code

6. patch Méthodes

// patch.js
export const patch = (prev, next, parent) => { 
     // type: 'div' -> type: 'p'  
     // typeC'est différent,removeVieux., Monter un nouveau 
     if (prev.type !== next.type) {   
           parent.removeChild(prev.el)   
           mount(next, parent)   
           return 
      }  
     // type C'est pareil,diff props(Ne pense pas. children) 
     const { props: { children: prevChildren, ...prevProps } } = prev  
     const { props: { children: nextChildren, ...nextProps } } = next  
     // patchProps  
     const el = (next.el = prev.el) 
     // patch  Besoin de mettre à jour le noeud props
     for (let key of Object.keys(nextProps)) {
         let prev = prevProps[key],    
         let next = nextProps[key]    
         patchProps(key, prev, next, el) 
     }  
     // patch  De l'ancien noeud props,  De l'ancien noeud propsDekey Il n'y a pas de nouveau noeud ,Directremove Ces vieux noeuds props
     for (let key of Object.keys(prevProps)) {   
         if (!nextProps.hasOwnProperty(key)) patchProps(key, prevProps[key], null, el) 
     }  
     // patch children 
     patchChildren(prevChildren, nextChildren, el)}
Copier le Code

7.patchChildren Méthodes

 // patch.js
 import { odiff } from './optimization-diff.js'
  const patchChildren = (prev, next, parent) => { 
     // diff  Comparer les performances de consommation , Vous pouvez faire quelques traitements avant ,Améliorer l'efficacité    //  S'il n'y a pas d'anciens noeuds 
   if (!prev) {   
        if (!next) {   
           //  En même temps, il n'y a pas de nouveaux et anciens noeuds , Ça n'arrivera pas   
         }   
         else {  
            next = Array.isArray(next) ? next : [next]   
            for (const c of next) {    
                 //  Monter directement l'ancien noeud 
                  mount(c, parent)   
             }   
         }
   } 
   // Un seul children 
   else if (prev && !Array.isArray(prev)) {   
          if (!next) parent.removeChild(prev.el)   
          else if (next && !Array.isArray(next)) {   
             patch(prev, next, parent)  
          }
          else {     
             parent.removeChild(prev.el)    
             for (const c of next) {     
               mount(c, parent)    
             }    
         }  
   }  else odiff(prev, next, parent)}
Copier le Code

版权声明
本文为[Xi Chen]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/09/20210914164426009y.html

Scroll to Top