虚拟DOM及diff算法学习

h.js

import vnode from "./vnode";

export default function h(sel,data,c){
    if(arguments.length!=3){
        throw new Error('参数数量错误');
    }
    if(typeof c=='string'||typeof c=='number'){
        // console.log(123,c);
        return vnode(sel,data,undefined,c,undefined);
    }
    else if(Array.isArray(c)){
        let children=[];
        for(let i =0;i<c.length;i++){
            if(!(typeof c[i]=='object' && c[i].hasOwnProperty('sel'))){
                throw new Error('参数格式错误')
            }
            // console.log(c[i]);
            children.push(c[i]);
        }
        // console.log(111,vnode(sel,data,children,undefined,undefined));
        return vnode(sel,data,children,undefined,undefined);
    }
    else if(typeof c=='object' && c.hasOwnProperty('sel')){
        let children=[c]
        // console.log(222,vnode(sel,data,children,undefined,undefined));
        return vnode(sel,data,children,undefined,undefined);
    }
    else{
        throw new Error('参数格式错误')
    }
}

vnode.js

export default function(sel,data,children,text,elm){
    const key = data === undefined ? undefined : data.key
    return {
        sel,data,children,text,elm,key
    }
}

path.js

import vnode from "./vnode";
import createElement from "./createElement";
import pathVonde from "./pathVnode";
export default function path(oldVnode, newVonde) {
    if (oldVnode.sel == '' || oldVnode.sel == undefined) {
        oldVnode = vnode(oldVnode.tagName.toLowerCase(), {}, [], undefined, oldVnode);

    }
    if (oldVnode.sel == newVonde.sel && oldVnode.key == newVonde.key) {
        console.log('yes');
        pathVonde(oldVnode,newVonde);
    }
    else {
        console.log('no');
        let newVondeElm = createElement(newVonde, oldVnode.elm);
        // 插入老节点之前
        if (oldVnode.elm.parentNode && newVondeElm) {
            oldVnode.elm.parentNode.insertBefore(newVondeElm, oldVnode.elm)
        }
        // 删除老节点
        oldVnode.elm.parentNode.removeChild(oldVnode.elm)
    }
}

createElement.js

export default function createElement(vnode,pivot){
    // console.log(vnode,pivot);
    // 创建一个DOM节点
    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>0){
        // 子节点
        for(let i =0;i< vnode.children.length;i++){
            let ch=vnode.children[i];
            let chdom=createElement(ch);
            domNode.appendChild(chdom);
        }
    }
    // 补充elm属性 
    vnode.elm=domNode;
    return vnode.elm
}

pathVnode.js

import createElement from'./createElement.js';
import updateChildren from './updateChildren.js';
export default function pathVonde(oldVnode,newVonde){
    // 新旧一样
    if (oldVnode === newVonde) {
        console.log('新旧一样');
        return
    }
    // 新vnode是否含有text
    if (newVonde.text != '' && (newVonde.children == undefined || newVonde.children.length == 0)) {
        console.log('新vnode含有text');
        // 含有text
        // console.log(oldVnode,newVonde);
        if (oldVnode.text != newVonde.text) {
            oldVnode.elm.innerText = newVonde.text;
        }
    }
    else {
        console.log('不含有text');
        // 不含有text
        // 老节点是否含有children
        if (oldVnode.children != undefined && oldVnode.children.length  > 0) {
            console.log('老节点含有children');
            // 含有children
            // console.log(oldVnode,newVonde);
            updateChildren(oldVnode.elm,oldVnode.children,newVonde.children);
        }
        else {
            // 不含有children
            console.log('不含有children');
            oldVnode.elm.innerHTML='';
            for(let i =0; i<newVonde.children.length;i++){
                let dom = createElement(newVonde[i]);
                oldVnode.elm.appendChild(dom);
            }
        }
    }
}

updateChildren.js

import createElement from "./createElement.js";
import pathVonde from "./pathVnode.js";

function checkSameVnode(a, b) {
    // console.log(a,b);
    return a.sel == b.sel && a.key == b.key;
}
export default function updateChildren(parentElm, oldCh, newCh) {
    // 旧前
    let oldStartIdx = 0;
    // 新前
    let newStartIdx = 0;
    // 旧后
    let oldEndIdx = oldCh.length - 1;
    let oldEndIdx2 = oldCh.length - 1;
    // 新后
    let newEndIdx = newCh.length - 1;
    let newEndIdx2 = newCh.length - 1;
    // 旧前节点
    let oldStartVnode = oldCh[0];
    // 新前节点
    let newStartVnode = newCh[0];
    // 旧后节点
    let oldEndVnode = oldCh[oldEndIdx];
    // 新后节点
    let newEndVnode = newCh[newEndIdx];
    let keymap = null;
    let i=0;
    while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
        console.log('第',++i,'次循环');
        if (oldStartVnode == null || oldCh[oldStartIdx] == undefined) {
            console.log('break1');
            oldStartVnode = oldCh[++oldStartIdx];
        }
        else if (oldEndVnode == null || oldCh[oldEndIdx] == undefined) {
            console.log('break2');
            oldEndVnode = oldCh[--oldEndIdx];
        } else if (newStartVnode == null || newCh[newStartIdx] == undefined) {
            console.log('break3');
            newStartVnode = newCh[++newStartIdx];
        }
        else if (newEndVnode == null || newCh[newEndIdx] == undefined) {
            console.log('break4');
            newEndVnode = newCh[--newEndIdx];
        }
        else if (checkSameVnode(oldStartVnode, newStartVnode)) {
            console.log('新前旧前',newStartIdx,oldStartIdx);
            pathVonde(oldStartVnode, newStartVnode);
            oldStartVnode = oldCh[++oldStartIdx];
            newStartVnode = newCh[++newStartIdx];
        }
        else if (checkSameVnode(oldEndVnode, newEndVnode)) {
            console.log('新后旧后',newEndIdx,oldEndIdx);
            pathVonde(oldEndVnode, newEndVnode);
            oldEndVnode = oldCh[--oldEndIdx];
            newEndVnode = newCh[--newEndIdx];
        }
        else if (checkSameVnode(oldStartVnode, newEndVnode)) {
            console.log('新后旧前',newEndIdx,oldStartIdx);
            pathVonde(oldStartVnode, newEndVnode);
            parentElm.insertBefore(oldStartVnode.elm, oldStartVnode.elm.nextSibling);
            oldStartVnode = oldCh[++oldStartIdx];
            newEndVnode = newCh[--newEndIdx];
        }
        else if (checkSameVnode(oldEndVnode, newStartVnode)) {
            console.log('新前旧后',newStartIdx,oldEndIdx);
            pathVonde(oldEndVnode, newStartVnode);
            parentElm.insertBefore(oldEndVnode.elm, oldStartVnode.elm);
            oldEndVnode = oldCh[--oldEndIdx];
            newStartVnode = newCh[++newStartIdx];
        }
        else {
            console.log('都不匹配');
            if (!keymap) {
                console.log('!keymap');
                keymap = {}
                for (let i = oldStartIdx; i < oldEndIdx; i++) {
                    const key = oldCh[i].key;
                    if (key != undefined) {
                        keymap[key] = i;
                    }
                }
            }
            const idxInOld = keymap[newStartVnode.key]
            if (idxInOld == undefined) {
                console.log('idxInOld == undefined');
                // 全新项
                parentElm.insertBefore(createElement(newStartVnode),oldStartVnode.elm)
            }
            else {
                console.log('idxInOld != undefined');
                // 不是全新项,需要移动
                const elmToMove = oldCh[idxInOld];
                pathVonde(elmToMove, newStartVnode);
                // 标记为处理过
                oldCh[idxInOld] = undefined;
                // 移动
                parentElm.insertBefore(elmToMove.elm, oldStartVnode.elm);
            }
            newStartVnode = newCh[++newStartIdx]
        }
    }
    console.log('新前:',newStartIdx,'新后:',newEndIdx);
    console.log('旧前:',oldStartIdx,'旧后:',oldEndIdx);
        if (newStartIdx <= newEndIdx) {
            console.log('newStartIdx <= newEndIdx');
            const before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm;
            // const before = oldCh[oldEndIdx] == null ? null : oldCh[oldEndIdx].elm;
            console.log(before);
            for (let i = newStartIdx; i <=newEndIdx ; i++) {
                parentElm.insertBefore(createElement(newCh[i]), before);
            }
        }
        else if (oldStartIdx <= oldEndIdx) {
            console.log('oldStartIdx <= oldEndIdx');
            // 批量删除
            for (let i = oldStartIdx; i <= oldEndIdx; i++) {
                if (oldCh[i]) {
                    parentElm.removeChild(oldCh[i].elm)
                }
            }
        }
}

index.js

// 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
// ]);

import patch from './mysnabbdom/patch.js';
import h from './mysnabbdom/h.js';
const container=document.getElementById('container')

// document.getElementById
// document.getElementsByClassName
// document.getElementsByName
// document.getElementsByTagName
// document.getElementsByTagNameNS
// const myVonde1=h('h1',{},'hello')

// const myVonde1 = h('ul', {}, [
//     h('li', { key: 'A' }, 'A'),
//     h('li', { key: 'B' }, 'B'),
//     h('li', { key: 'C' }, 'C'),
//     h('li', { key: 'D' }, 'D'),
//     h('li', { key: 'E' }, 'E'),
// ])
// patch(container, myVonde1);
// const vnode2 = h('ul', {}, [
//     h('li', { key: 'Q' }, 'Q'),
//     h('li', { key: 'T' }, 'T'),
//     h('li', { key: 'E' }, 'E'),
//     h('li', { key: 'B' }, 'B'),
//     h('li', { key: 'A' }, 'A'),
//     h('li', { key: 'D' }, 'D'),
//     h('li', { key: 'C' }, 'C'),
//     h('li', { key: 'V' }, 'V'),
//     h('li', { key: 'Z' }, 'Z'),
// ])

// const myVonde1 = h('ul', {}, [
//     h('li', { key: 'A' }, 'A'),
//     h('li', { key: 'B' }, 'B'),
//     h('li', { key: 'C' }, 'C'),
//     h('li', { key: 'D' }, 'D')
// ])
// patch(container, myVonde1);
// const vnode2 = h('ul', {}, [
//     h('li', { key: 'E' }, 'E'),
//     h('li', { key: 'A' }, 'A'),
//     h('li', { key: 'B' }, 'B'),
//     h('li', { key: 'E' }, 'E'),
//     h('li', { key: 'C' }, 'C'),
//     h('li', { key: 'D' }, 'D'),
//     h('li', { key: 'E' }, 'E'),
// ])
btn.onclick = function () {
    patch(myVonde1, vnode2);
}
最后修改:2021 年 09 月 23 日
如果觉得我的文章对你有用,请随意赞赏