You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
后记:
梳理的比较简单,很多细节我没有精力作一一的总结,因为我自己看源码看了好久,代码中涉及到很多异步,事务等等干扰项,然后我自己又不想过多的借助现有的资料-_-。当我快要把最后一点写完的时候发现 pure render 专栏的作者陈屹出了一本《深入React技术栈》里面有相当详细的源码分析,所以我感觉我这篇“白写”了,贴出这本书就可以了,不过陈屹的这本书是良心之作,必须安利下。
The text was updated successfully, but these errors were encountered:
React 更新视图过程
真实的 setState 的过程:
updateComponent 方法的说明:
接下来看下 shouldUpdateReactComponent 方法:
基本的思路就是比较当前 vdom 节点的类型,如果一致则更新,如果不一致则重新生成一份新的节点替换掉原来的。好了回到刚刚跟新 dom node这条路 prevComponentInstance.receiveComponent( nextRenderedElement ),即 render 里面根元素的渲染对象的 receiveComponent 方法做了最后的更新 dom 的工作。如果根节点的渲染对象是组件即 ReactCompositeComponent.receiveComponent,如果根节点是内置对象(html 元素)节点即 ReactDOMComponent.receiveComponent。ReactCompositeComponent.receiveComponent 最终还是调用的上面提到的 updateComponent 循环去生成 render 中的 vdom,这里就先不深究了。最终 html dom node 的更新策略都在 ReactDOMComponent.receiveComponent 中。
this.updateChildren( nextChildren, transaction, context )
中是真正的 diff 算法,就不以代码来说了(因为光靠代码很难说明清楚)先来看最简单的情况:
例A:
按节点顺序开始遍历 nextChildren(遍历的过程中记录下需要对节点做哪些变更,等遍历完统一执行最终的 dom 操作),相同位置如果碰到和 prevChildren 中 tag 一样的元素认为不需要对节点进行删除,只需要更新节点的 attr,如果碰到 tag 不一样,则按照新的 vdom 中的节点重新生成一个节点,并把 prevChildren 中相同位置老节点删除。按以上两个状态的 vdom tree,那么遍历完就会记录下需要做两步 dom 变更:新增一个 span 节点插入到第二个位置,删除原来第二个位置上的 div。
再来看两个例子:
例B:
遍历结果:第二个节点新增一个span,删除第二个div和第四个div。
例C:
遍历结果:第二个节点新增一个span,第四个节点新增一个div,删除第二个div。
我们看到对于例C来说其实最便利的方法就是把 span 插入到第二的位置上,然后其他div只要做 attr 的更新而不需要再进行位置的增删,如果 attr 都没有变化,那么后两个 div 根本不需要变化。但是按例A里面的算法,我们需要进行好几步的 dom 操作。这是为算法减少时间复杂度,做了妥协。但是 react 对节点引入了 key 这个关键属性帮助优化这种情况。假设我们给所有节点都添加了唯一的 key 属性,如下面例D:
例D:
我们在遍历过程中对所要记录的东西进行优化,在某个位置碰到有 key 的节点我们去 prevChildren 中找有没有对应的节点,如果有,则我们会比较当前节点在前后两个 tree 中相对位置。如果相对位置没有变化,则不需要做dom的增删移,而只需要更新。如果位置不一样则需要记录把这个节点从老的位置移动到新的位置(具体算法需要借助前一次dom变化的记录这里不详述)。这样从例C到例D的优化减少了 dom 节点的增删。
但是 react 的这种算法的优化也带来了一种极端的情况:
例E:
遍历结果:3次节点位置移动:2到1,1到2,0到3。
但是其实这里只需要更新每个节点的 attr,他们的位置根本不需要做变化。所以如果要给元素指定 key 最好避免元素的位置有太多太大的跃迁变化。
基本上 setState 之后到最终的 dom 变化的过程就是这么结束了。
The text was updated successfully, but these errors were encountered: