Diff算法2

diff的流程图

h函数,目的是创建出虚拟节点,然后准备上树

import vnode from './vnode.js'

// 低配版h函数,必须接受3个参数,作用于重载功能,调用的形态必须是下面的三种之一
// 形态1    h('div',{},'文字')
// 形态2    h('div',{},[])
// 形态3    h('div',{},h())
export default function(sel,data,c){
    // 检查参数的个数
    if(arguments.length!=3){
        throw new Error('对不起,h函数必须传入三个参数,我们是低配版h函数')
    }
    //检查参数c的类型
    if(typeof c=='string' || typeof c=='number'){
        // 说明现在调用h寒素是形态1
        return vnode(sel,data,undefined,c,undefined)
    } else if(Array.isArray(c)){
        let children = []
        // 说明现在调用h函数是形态2
        // 遍历
        for(let i=0;i<c.length;i++){
            // c[i]必须是一个对象,如果不满足
            if(!(typeof c[i]=='object' && c[i].hasOwnProperty('sel'))) throw new Error('传入的数组参数中有不是h函数的项')
            // 每一项都执行h函数,此时只需收集
            children.push(c[i])
        }
        // children收集完毕返回虚拟dom
        return vnode(sel,data,children,undefined,undefined)
    } else if(typeof c=='object' && c.hasOwnProperty('sel')){
        // 形态3
        // 传入的c是唯一的数组
        let children = [c]
        return vnode(sel,data,children,undefined,undefined)
    }else {
        throw new Error('传入的第三个参数类型不对')
    }
}

vnode.js

export default function(sel,data,children,text,elm){
    return {
        sel,data,children,text,elm
    }
}

直接返回一个对象,包含如下:

sel        标签名,如div

data        一个对象,对象里头可以是key,可以是props属性,可以是class等等

children        子节点,children可以是文本,可以是数组,数组下还可以是虚拟dom

text        表示如div下的文本

elm        表示当前节点

path.js用于上树,第一个参数oldVnode是老节点,newVnode是即将上树的新节点

import vnode from "./vnode"
import createElement from "./createElement";
export default function patch(oldVnode,newVnode){
    //判断传入的第一个参数是DOM节点还是虚拟节点
    if(oldVnode.sel == '' || oldVnode.sel == undefined){
        // 传入的第一个参数是DOM节点,此时包装为虚拟节点
        oldVnode = vnode(oldVnode.tagName.toLowerCase(),{},[],undefined,oldVnode);
    } 
    console.log(oldVnode);
    // 判断oldVnode和newVnode是不是同一个节点
    if(oldVnode.key==newVnode.key&& oldVnode.sel==newVnode.sel){
        console.log("是同一个节点")
    }else{
        console.log("不是同一个节点,暴力插入新的,删除旧的")
        let newVnodeElm = createElement(newVnode)
        // 出入到老节点之前
        if(oldVnode.elm.parentNode!=undefined && newVnodeElm){
            oldVnode.elm.parentNode.insertBefore(newVnodeElm,oldVnode.elm);
        }
        console.log(oldVnode)
    }
}

createElement.js        接收一个新的虚拟节点,里面包含sel,data,children,text,elm

目的是把虚拟节点变为真正的DOM节点

// 真正创建节点,将vnode创建为DOM,是孤儿节点,不进行插入
export default function createElement(vnode){
    console.log('目的是把虚拟节点',vnode,'变为真正的DOM节点')
    // 创建一个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]
            // 创建出它的DOM,一旦调用createElement意味着:创建出DOM,并它的elm属性指向了创建出的DOM,但是还没有上树,是一个孤儿节点
            let chDom = createElement(ch)
            domNode.appendChild(chDom)
        }
        console.log(domNode)
    }
    vnode.elm = domNode
    return vnode.elm
}

这里用到了递归,因为虚拟dom下还有子节点的虚拟DOM,就要不断的自我调用,将新的children下的子节点传入createElement下,然后将下一层的节点继续添加,最后返回这个总的DOM节点。

得到如下:

 在旧节点前调用insertBefore就能成功上树。

index.js

import h from './mysnabbodm/h'
import patch from './mysnabbodm/patch'

// const myVnode = h('h1',{},'你好')
const myVnode1 = h('ul',{},[
    h('li',{},'A'),
    h('li',{},'B'),
    h('li',{},[
        h('div',{},'aaa')
    ]),
    h('li',{},'D')
])
const container = document.getElementById('container')

patch(container,myVnode1)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值