Java教程

虚拟DOM 和 diff 算法 ——— 感受 diff 算法(第一次上树)

本文主要是介绍虚拟DOM 和 diff 算法 ——— 感受 diff 算法(第一次上树),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

 一、感受 diff 算法

 

 

 当父节点发生改变时,比如 ul 变为 ol ,里面的 li 不发生改变,diff 算法是会暴力删除的。

 

 

 

2. diff 算法处理新旧节点不是同一个节点时。

snabbdom 判断是否是相同的虚拟节点:

 

 

 

 创建节点时,所有子节点需要递归创建的。

二、手写第一次上树时

1. 目录结构:

 

 

// index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id='btn'>点我改变DOM</button>
    <div id="container"></div>
    <script src="/virtual/bundle.js"></script>
</body>
</html>
// index.js
import h from './mySnabbdom/h'
import patch from './mySnabbdom/patch'
// var myVnode1 = h('div', {}, 'test')
// var myVnode1 = h('div', {}, [])
// var myVnode1 = h('div', {}, h())
// var myVnode1 = h('div', {}, [
//     h('div', {}, '菠萝'),
//     h('div', {}, '香蕉'),
//     h('div', {}, [
//         h('div', {}, '火龙果'),
//         h('div', {}, '牛油果'),
//     ]),
// ])

const container = document.getElementById('container');
const btn = document.getElementById('btn');
var myVnode1 = h('ul', {}, [
    h('li', {}, '火龙果'),
    h('li', {}, [
        h('div', {}, [
            h('ol', {}, [
                h('li', {}, '哈哈哈'),
                h('li', {}, '嘿嘿嘿'),
                h('li', {}, '呵呵呵'),
            ]),
        ]),
        
    ]),
]);
patch(container, myVnode1);

var myVnode2 = h('div', {}, [
    h('h1', {}, '你好'),
    h('h2', {}, '再见'),
]);
btn.onclick = function() {
    patch(myVnode1, myVnode2);
};

 

// patch.js
import vnode from './vnode'
import createElement from './createElement';
function isVnode(vnode) {
    return typeof(vnode.sel) !== 'undefined';
}
function isSameNode(oldVnode, newVnode) {
    return oldVnode.key === newVnode.key && oldVnode.sel === newVnode.sel;
}
export default function(oldVnode, newVnode) {
  // 判断 oldVnode 是虚拟节点还是 DOM 节点
  if (!isVnode(oldVnode)) {
      // oldVnode 是 container 节点,tagName 属性是大写
      // 如果是 DOM 节点,就包装成虚拟节点
      oldVnode = vnode(oldVnode.tagName.toLowerCase(), {}, [], undefined, oldVnode); 
      // sel, data, children, text, elm
  }
  // 判断 oldVnode 和 newVnode 是不是同一个节点 (key和 选择器sel 相同)
  if (isSameNode(oldVnode, newVnode)) {
    console.log('是同一个节点');
  } else {
      // 暴力插入新的,删除旧的
      let newVnodeElm = createElement(newVnode, oldVnode.elm);
      // 插入到老节点之前
      if (oldVnode.elm.parentNode && newVnodeElm) {
          oldVnode.elm.parentNode.insertBefore(newVnodeElm, oldVnode.elm);
      }
      // 删除老节点
      oldVnode.elm.parentNode.removeChild(oldVnode.elm);
  }
}
// createElement.js
// 真正创建节点,将 vnode 创建为 DOM, 孤儿节点,不插入
export default function createElement(vnode) {
    // 标杆:pivot,在标杆前面插入新节点 vnode
    let domNode = document.createElement(vnode.sel);
    // 有子节点还是文本
    if (vnode.text !== '' && (vnode.children == undefined || vnode.children.length == 0)) {
        // 内部是文字
        domNode.innerText = vnode.text;
    } else if (Array.isArray(vnode.children) && vnode.children.length) {
        vnode.children.forEach(item => {
            let childVnodeElm = createElement(item);
            domNode.appendChild(childVnodeElm);
        })
    }
    vnode.elm = domNode; // 补充 elm 属性
    return vnode.elm;
}

2. 界面效果:

 

 

这篇关于虚拟DOM 和 diff 算法 ——— 感受 diff 算法(第一次上树)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!