virtual dom

最近研究 virtual dom 死了大概十几亿脑细胞…

前几天在juejin上看这篇文章,按照作者的思路实现了一遍,写的很好,主要是 virtual dom 思路。这里我就不再详述了,原文很详细。
这里我讲一下我碰到的问题,删除节点会有问题,且没有实现增加节点的功能,特别是删除节点的逻辑,下面是代码:

childNodes.forEach(child => walk(child));

if (current) {
     doPatch(node, current); // 打上补丁
 }

如果删除节点,不管是先执行子元素的递归还是先操作元素本身,都会出现问题,循环的会出现index不匹配的问题,我怎么改都有问题,感觉完全没有思路。


后面有个想法,有没有可能不通过index的方式去匹配,通过对象去绑定。正好最近在看 WeakMap,它的 key值是对象而且是弱引用,而且它不会阻扰垃圾回收机制,换句话说如果key值被删了,那key值所关联的数据也会被释放掉。如果是key值是节点,如果元素被删除了,WeakMap key值会被回收,关键的数据也同样被释放

// html
<div>
	<p id='p'>p</p>
</div>

//javascript
const wm = new WeakMap()
wm.set(document.querySelector('#p'), '111')
console.log(wm.get(document.querySelector('#p'))); // 11
document.querySelector('#p').parentNode.removeChild(document.querySelector('#p')); // 元素被清除
console.log(wm.get(document.querySelector('#p'))); // undefined

上面的代码中,元素被移除后,无法通过key值去获取数据
WeakMap key值适合可能会消失的对象,因为不会影响垃圾回收


回到diff算法,如果把dom元素设置为key值,元素对应的对象为value。在创建元素的时候将元素与对象进行关联,在进行diff的时候将diff类型绑定到元素上,patches的时候通过WeakMap获取对应的diff类型,最后执行补丁修改

// element.js
function render(dom, wm) {
  let el = document.createElement(dom.type)
	
  wm.set(el, dom)

  for (let key in dom.props) {
    setAttrs(el, key, dom.props[key])
  }

  dom.children.forEach(child => {
    child = child instanceof Element ? render(child, wm) : createTextNode(child, wm)
    el.appendChild(child)
  })

  return el
}

// diff.js
function walk(oldNode, newNode, parent) {
  if (!newNode) {
    oldNode.diff = [{ type: 'REMOVE' }]
  } else if (!oldNode) {
    if (!parent.diff) {
      parent.diff = []
    }
    parent.diff.push({ type: 'ADD', newNode })
  } else if (isStringOrNumber(oldNode) && isStringOrNumber(newNode)) {
    if (oldNode !== newNode) {
      if (!parent.diff) {
        parent.diff = []
      }
      parent.diff.push({ type: 'TEXT', text: newNode })
    }
  } else if (oldNode.type === newNode.type) {
    let attr = diffAttr(oldNode.props, newNode.props)
    if (Object.keys(attr).length > 0) {
      oldNode.diff = [{ type: 'ATTR', attr }]
    }
    diffChildren(oldNode.children, newNode.children, oldNode)
  } else {
    oldNode.diff = [{ type: 'REPLACE', newNode }]
  }
}

// patches.js
function(node, wm) {
	const virtualDom = wm.get(node) || {}
	const { diff } = virtualDom
	
	if (diff) {
	 	doPatch(node, diff, wm)
	}
	
	if (!isInPage(node)) {
    	return
  	}
  	...
}

完整代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值