自己写一个 react 4.domdiff

发布于 2022-02-21 13:47:47 字数 3336 浏览 1012 评论 0

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。我会先覆盖以下测试用例

  1. 同一类型节点,内容(textNode)有变化
  2. 同一类型节点,属性有变化
  3. 不同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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84961 人气
更多

推荐作者

胡图图

文章 0 评论 0

zt006

文章 0 评论 0

z祗昰~

文章 0 评论 0

冰葑

文章 0 评论 0

野の

文章 0 评论 0

天空

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文