自己写一个 react 4.domdiff
react 的出现,解决了前端一直以来的几个痛点,一个是组件化,一个是 dom 的更新效率问题。domdiff 算法是后者的关键。
这一步的更新持续时间比较久,代码行数也从 1xx 变成了 4xx,参考了 react 和一些迷你 react 的框架,下面把一些要点记录一下。
大思路
这里面有两条思路分支,到底我们是先把新旧的虚拟 dom 都生成好,比较完再去应用 dom 上(dom Patches) ,还是直接在真实 dom 上面遍历,一边比较一边应用差异呢。
官方是用前者,一些迷你 react 框架是用后者。在我看来,前者其实是一种分层处理的思想。state处理层->虚拟dom比较层->dom Patch层
, 每一层之间的对接保持一致,那么每层内部就能在一个小的关注点里面进行优化,就好像TCP/IP分层那样。 当然这样的设计肯定会有一定的性能损耗和代码冗余。我觉得关键在于你对于框架代码的定位如何,如果是定位比较大,建议分层。如果是针对当前具体业务问题快速解决,那把所有的更新写在一起会更加快捷小巧。
我的目的是学习官方的思路,所以我选择了前者。
dom Patches
在实测过程中经常有 state1->state2 好好的,state2->state3 不符合预期的问题,细节断点调试之后,发现原因一般是dom的Patches没做好。针对真实dom,经常会断链,针对虚拟dom,一般是局部属性的增删改没同步好。
针对react组件,可以看成以container为根的一棵大dom树,对树或者链表进行操作的时候经常会遇到断链的问题,比如在textNode在更新时,要针对于包裹它的element进行,否则会断链,后续节点的更新就跟踪不了。而且也会发现更新真实dom后忘记更新虚假dom的情况,在state2->state3时,实际上是state1->state3,会发生各种超出预期的情况。
简化问题,循序渐进
事实上,dom节点的变化可以非常大,之前还是一个textNode,后面可能会变成一个列表,列表有可能有些有 key,有些无 key。我会先覆盖以下测试用例
- 同一类型节点,内容(textNode)有变化
- 同一类型节点,属性有变化
- 不同tagName节点
把每一个局部问题解决,再串一起解决
列表问题,回顾 createDocumentFragment/children 问题
对于列表的更新问题是domdiff中的难点,也是react要建立key属性的重要原因。其实列表的更新也可以非常复杂,我先简化为三点,删、增、更换顺序。再逐个击破,在代码测试用例中的体现就是
<ul>
{
a ? [
<li key="1">1</li>,
<li key="2">2</li>,
<li key="3">3</li>,
]: [
<li key="1">1</li>,
<li key="2">2</li>,
]
}
</ul>
<hr/>
<ul>
{
a ? [
<li key="1">1</li>,
<li key="2">2</li>,
<li key="3">3</li>,
]: [
<li key="1">1</li>,
<li key="2">2</li>,
<li key="3">3</li>,
<li key="4">4</li>,
]
}
</ul>
<hr/>
<ul>
{
a ? [
<li key="1">1</li>,
<li key="2">2</li>,
<li key="3">3</li>,
]: [
<li key="1">1</li>,
<li key="3">3</li>,
<li key="2">2</li>,
]
}
</ul>
在这里提一下之前的createDocumentFragment问题,其实一开始会想到利用这个api是因为一个列表总需要一个容器,但是逻辑上,我们在渲染一个元素的时候,如果发现它的子元素是个列表,那么这个元素的dom就是子元素的容器,并不需要额外再创建容器。
具体的实现看代码 https://github.com/p2227/diyReact/blob/ed45e6be54dc95b76f0a755352118eb1ed3ea890/src/stage3-domdiff.js ,就不细说了,要说明一下,这个方法并不是最优,但是自己先尝试实现,再带着问题参考源代码,受益匪浅。在实现过程中也是参考也非常多的资料,基本上是自己在8小时以外挤时间处理的。
心得
带着问题阅读源代码,你才能学到东西
如果你觉得痛苦,说明你是在成长
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论