Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

虚拟DOM之更新子节点(一) #24

Open
chenqf opened this issue May 12, 2020 · 0 comments
Open

虚拟DOM之更新子节点(一) #24

chenqf opened this issue May 12, 2020 · 0 comments

Comments

@chenqf
Copy link
Owner

chenqf commented May 12, 2020

虚拟DOM之更新子节点(一)

回顾

在对比子节点差异的时候,我们需要区分新旧子节点的不同情况:

  • 旧:单个子节点 && 新:单个子节点
  • 旧:单个子节点 && 新:没有子节点
  • 旧:单个子节点 && 新:多个子节点
  • 旧:没有子节点 && 新:单个子节点
  • 旧:没有子节点 && 新:没有子节点
  • 旧:没有子节点 && 新:多个子节点
  • 旧:多个子节点 && 新:单个子节点
  • 旧:多个子节点 && 新:没有子节点

这8中情况,都非常简单,就不过多论述了。

再先看一下,我们之前是怎么操作新旧节点均为多个子节点的情况的:

遍历旧的子节点,将其全部移除:

for (let i = 0; i < prevChildren.length; i++) {
    removeChild(container,prevChildren[i].el)
}

遍历新的子节点,将其全部挂载

for (let i = 0; i < nextChildren.length; i++) {
    mount(nextChildren[i], container)
}

在浏览器中操作DOM是极为昂贵的,这种操作方式需要不停的对DOM进行删除和创建的操作,但是对于新旧节点差异很小的情况下,无法尽可能的复用原有DOM节点,从而造成性能上的浪费。

看一个简单的例子:

按照之前的逻辑,原有子节点删除,重新创建所有的新子节点,无疑会造成性能上的巨大浪费。

我们真正想做的,其实是找出二者之间的区别,针对二者的不同进行修改。

找出新旧子节点的不同,并针对不同之处进行更新的过程,其实就是我们常说的核心Diff算法。

React/Vue中的key

Vue对于Key的说明:

key 的特殊属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。

React对于Key的说明:

当子元素拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素。

总结

新子节点根据自己的key找到对应的旧子节点。

通过key来查找新旧子节点的对应关系其实很好理解,但不论是React还是Vue,都支持不传递Key的使用方式,那么这种方式,新旧子节点如何查找对应关系的呢?

无Key的Diff

在没有key的时候,为了能够复用原有的dom,所以我们需要通过新旧子节点的顺序来进行比对。

当新旧子节点长度相同时:

当新子节点长度大于旧子节点长度:

当新子节点长度小于旧子节点长度:

简单代码:

const noKeyDiff = function(prevChildren,nextChildren,container){
    //旧长度
    let prevLen = prevChildren.length;
    //新长度
    let nextLen = nextChildren.length;
    //最小长度
    let commonLen = Math.min(prevLen,nextLen);
    //长度相同
    for(let i = 0;i<commonLen;i++){
        patch(prevChildren[i],nextChildren[i],container)
    }
    //新长大于旧长,挂载
    if(nextLen > prevLen){
        for(let i = commonLen; i<nextLen; i++){
            mount(nextChildren[i],container);
        }
    }
    //新长小于旧长,删除
    else if(prevLen > nextLen){
        for(let i = commonLen; i<prevLen; i++){
            removeChild(container,prevChildren[i].el)
        }
    }
}

无key的好处

对于简单DOM的情况,性能更好~

因为至于一次循环,就处理了所有的新旧子节点,当key存在的时候,无论使用何种diff优化算法,都不可避免的需要进行多重循环来进行key的比对。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant