download:高级前端进阶必修:自主打造高扩展的业务组件库.
.
序
都说“双端比较算法”,那么双端比较算法是什么样的呢?和React中的diff
算法有什么不同?
要理解这一点,首先要了解React中的Diff算法,然后是Vue3中的Diff算法,最后说说Vue2中的Diff算法,从而比较两者的区别。
最后说一下为什么Vue中不需要使用光纤架构。
对官方分析做出反应
其实React为什么不用Vue的双端比较算法?React官方已经在源代码的评论里解释过了。让我们来看看React官方是怎么说的。
函数reconcileChildrenArray(
returnFiber:纤维,
currentFirstChild: Fiber | null,
newChildren:数组,
expirationTime: ExpirationTime,
):光纤|空值{
//这个算法不能通过从两端搜索来优化,因为我们
//在纤程上不要有反向指针。我想看看我们能走多远
//用那个型号。如果最终不值得权衡,我们可以
//以后再加。
//即使是两端优化,我们也希望针对这种情况进行优化
//在很少更改的情况下,强行进行比较,而不是
//去拿地图。它想探索先走这条路
//仅向前模式,并且仅在我们注意到需要时才使用地图
//很多前瞻。这不像双端那样处理反转
//搜索但那很不寻常。此外,对于两端优化来说
//处理Iterables,我们需要复制整个集合。
//在第一次迭代中,我们将遇到最坏的情况
//(将所有内容添加到地图中)对于每次插入/移动。
//如果更改此代码,也要更新reconcileChildrenIterator()
//使用相同的算法。
}
复制代码
大概意思是:
React无法通过双端比较优化Diff算法是因为目前纤程上没有反向链表,想知道这个方案能坚持多久?如果当前模式不理想,那么可以添加双端比较算法。
即使是双端比较算法,我们也要对这种情况进行优化。我们要用Map这种数据结构方案来代替原来那种改动不大的暴力比较的方案。它的第一个搜索周期是通过仅向前模式(即,它仅从左向右搜索)。(第一个周期可能还没结束,还有节点没对比。)如果要继续在正向循环中搜索,就必须使用数据类型映射。(就目前单向链表的数据结构而言,如果采用的话),双端比较搜索算法很难控制其反向搜索,但确实是一个成功的算法。另外,双端比较算法的实现也在我们的工作迭代中。
第一次迭代,就凑合用这个烂方案吧。每次添加/移动时,我们都必须将所有数据添加到Map数据类型对象中。
“我们需要复制整套”,这句话,我知道每个字,但我不知道他想说什么,所以我不会翻译它。有大神知道吗?
本人水平有限,错漏在所难免。如有错漏,请指正。
虽然React官方已经分析过了,但是要想彻底搞清楚为什么,我们还需要详细了解React的Diff算法是什么样的。在了解React Diff算法之前,我们首先要了解什么是纤程,为什么在React中使用纤程?
纤维的结构
在React15之前,通过更新React的组件来创建虚拟DOM和Diff的过程是不间断的。如果组件树的级别需要深度更新,Diff的进程会非常占用浏览器的线程,而我们都知道浏览器执行JavaScript的线程和渲染真实DOM的线程是互斥的,也就是说,在同一时间,浏览器要么在执行JavaScript代码操作,要么在渲染页面。如果JavaScript代码运行太长,页面就会卡住。
基于以上原因,React团队在React16之后重写了整个架构,把原来数组结构的虚拟DOM改成了一种叫纤程的数据结构。基于这种纤程数据结构,原来的不间断更新过程可以变成异步中断更新。
Fiber的数据结构主要看起来是这样的,Fiber的一些属性用来保存与组件相关的信息。
功能纤维节点(
标签:工作标签,
pendingProps:混合,
key: null | string,
模式:TypeOfMode,
) {
//作为静态数据结构的属性
this.tag = tag
this.key = key
this.elementType = null
this.type = null
this.stateNode = null
//用于连接其他纤程节点,形成纤程树。
this.return = null
this.child = null
this.sibling = null
this . index = 0;
this.ref = null
//作为动态工作单元属性
this . pending props = pending props;
this.memoizedProps = null
this.updateQueue = null
this.memoizedState = null
this.dependencies = null
this.mode = mode
this . effect tag = no effect;
this.nextEffect = null
this.firstEffect = null
this.lastEffect = null
//调度优先级关联
this.lanes = NoLanes
this.childLanes = NoLanes
//在另一次更新中指向该纤程对应的纤程。
this.alternate = null
}
复制代码
纤程主要由以下属性连接成树形结构数据,即纤程链表。
//指向父纤程节点
this.return = null
//指向子纤程节点
this.child = null
//指向右边的第一个同级纤程节点
this.sibling = null
复制代码
例如,以下组件结构:
函数App() {
返回(
我是
牛仔
)
}