We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
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
终于搞定了::><::感动我自己::><::
之前,我说,react 16 没有 diff,一堆人反驳我,甚至还有人给我截源码……
不过也确实,react 16 的算法,确实没找到特别合适的文章,看源码又十分要命
所以其实 fre 的排位算法,其实是大部分凭空复现,不过基本的精髓也有了
在阅读本篇文章之前,最好是对传统的 diff 有了解,没了解也没关系,只需要知道,它是俩 vdom 对比,而 children 的 比对是俩数组
然后数组就有索引,遍历完一个索引,就删掉,进行下一轮
但是 react 16 由于 Fiber 特殊的遍历形式,导致,不再有新旧 vdom 了
在 Fiber 的遍历过程,只有一个 fiber 节点在不断更替,我们通常称它为 WIP
然后就拿这个 WIP 去和 新的 vdom 比对
拿一个节点和一个嵌套的对象进行比对,很明显,想遍历没法遍历,想标号没法标号,可以说是没法儿比对的
所以我们想了个办法,首先,将 vdom 转化成 fiber
这个转换很简单,就是给 vdom 配上 child sibling return,就可以
比如:vdom 长这样:
let vdom = { type:'div', children:[ { type:'h1' } ] }
它需要被拍平,变成一个 fiber 节点,children 的 第一个为 child,第二个 为 child 的 sibling
let fiber = { type:'div', child:{ type:'h1' }, //... }
然后这样,就变成 两个 fiber 节点进行比对了,很不幸,还是不行,因为即便是拍平成一个对象了,这个对象没法儿遍历,不是我们想要的
所以我想了个更骚的方法,和 effectLink 一样,对 child 进行收集
每个 fiber 下面都有一个 childFibers 对象,它包含这个 fiber 下面所有的 child
let fiber = { type:'div', child:{ type:'h1' }, childFibers :{child1,child2,...childs} //... }
到这里,很多人会问,为什么不直接把 vdom 的 children 放上去?为什么需要是对象而不是数组?
我们看一个用例:
A B C -> A C
一旦有新增或者删除行为,如果是数组,它的索引会发生变化,比如,C 原来是 [2],因为 B 删掉了,C 变成 [1],B 找不到了
如果说这里有一一对应的遍历行为,也就是传统 diff,可以通过 index++ 或者 delete xxx 的方式控制索引的增减
所以关键点来了,fiber 并没有类似的遍历,自然控制不了索引
所以就需要对节点进行标号,对位置进行重排,这就是 react 16 核心算法所在
我们先给节点标号,fre 这里用的是 hash
children:{.0:child1,.1:child2,...childs}
我们手动给他标号,就不用担心索引的问题了,这里不能用数字作为键值,所以隐式转换成 hash
然后,针对新增和删除的行为,就这么愉快的搞定了
接下来就要搞更难的,位置的调换,比如这个用例:
A B -> B A
没有新增和删除,那么默认是更新,如果更新的话,要更新两次,但是很明显,只需要 B 插入 A 就可以了
我们要想知道,那些节点需要更换位置,需要对位置进行记录,所以每个 fiber 上,都有个 index
这里 A 的 index 是 0 变为 1,位置改变,需要插入,而不是更新,B 也是如此
但是不能插两次,所以我们规定,如果是是被插者,就不能插别人……
到这里,思考一下下面的用例:
A B C D -> B A D C
首先,B 插 A,A 被插了,不动,然后 C 插 D ,D 被插了,不动
很好,只需要两次,接着看:
A B C D -> C D A B
根据上面的算法,我们只能知道,C 插 D 和 B 插 A ,并不能知道 C D 和 A B 要不要换
所以我们再规定,如果说被插者和兄弟是一个人,就不能插,需要换个人
比如这里的 C 本来要插 D,但是发现 D 是自己的兄弟,那就换插 A
所以在这个用例中,C 插 A,D 也 插 A,更新两次,没毛病
至此,整个排位算法就差不多啦
具体的实现其实还是要考虑很多边界的,其实算法这东西,看实现和没意思
搞懂了本质,自己复现就很简单啦~
最后放上 fre 的 地址:https://github.com/132yse/fre
欢迎 star 与 fork ~
The text was updated successfully, but these errors were encountered:
我被你感动了
Sorry, something went wrong.
我也被你感动了
@zhanghao-zhoushan @zimv 哈哈哈嗝
No branches or pull requests
终于搞定了::><::感动我自己::><::
之前,我说,react 16 没有 diff,一堆人反驳我,甚至还有人给我截源码……
不过也确实,react 16 的算法,确实没找到特别合适的文章,看源码又十分要命
所以其实 fre 的排位算法,其实是大部分凭空复现,不过基本的精髓也有了
前提
在阅读本篇文章之前,最好是对传统的 diff 有了解,没了解也没关系,只需要知道,它是俩 vdom 对比,而 children 的 比对是俩数组
然后数组就有索引,遍历完一个索引,就删掉,进行下一轮
但是 react 16 由于 Fiber 特殊的遍历形式,导致,不再有新旧 vdom 了
在 Fiber 的遍历过程,只有一个 fiber 节点在不断更替,我们通常称它为 WIP
然后就拿这个 WIP 去和 新的 vdom 比对
局限
拿一个节点和一个嵌套的对象进行比对,很明显,想遍历没法遍历,想标号没法标号,可以说是没法儿比对的
所以我们想了个办法,首先,将 vdom 转化成 fiber
这个转换很简单,就是给 vdom 配上 child sibling return,就可以
比如:vdom 长这样:
它需要被拍平,变成一个 fiber 节点,children 的 第一个为 child,第二个 为 child 的 sibling
然后这样,就变成 两个 fiber 节点进行比对了,很不幸,还是不行,因为即便是拍平成一个对象了,这个对象没法儿遍历,不是我们想要的
所以我想了个更骚的方法,和 effectLink 一样,对 child 进行收集
每个 fiber 下面都有一个 childFibers 对象,它包含这个 fiber 下面所有的 child
到这里,很多人会问,为什么不直接把 vdom 的 children 放上去?为什么需要是对象而不是数组?
我们看一个用例:
一旦有新增或者删除行为,如果是数组,它的索引会发生变化,比如,C 原来是 [2],因为 B 删掉了,C 变成 [1],B 找不到了
如果说这里有一一对应的遍历行为,也就是传统 diff,可以通过 index++ 或者 delete xxx 的方式控制索引的增减
所以关键点来了,fiber 并没有类似的遍历,自然控制不了索引
所以就需要对节点进行标号,对位置进行重排,这就是 react 16 核心算法所在
我们先给节点标号,fre 这里用的是 hash
我们手动给他标号,就不用担心索引的问题了,这里不能用数字作为键值,所以隐式转换成 hash
然后,针对新增和删除的行为,就这么愉快的搞定了
接下来就要搞更难的,位置的调换,比如这个用例:
没有新增和删除,那么默认是更新,如果更新的话,要更新两次,但是很明显,只需要 B 插入 A 就可以了
我们要想知道,那些节点需要更换位置,需要对位置进行记录,所以每个 fiber 上,都有个 index
这里 A 的 index 是 0 变为 1,位置改变,需要插入,而不是更新,B 也是如此
但是不能插两次,所以我们规定,如果是是被插者,就不能插别人……
到这里,思考一下下面的用例:
首先,B 插 A,A 被插了,不动,然后 C 插 D ,D 被插了,不动
很好,只需要两次,接着看:
根据上面的算法,我们只能知道,C 插 D 和 B 插 A ,并不能知道 C D 和 A B 要不要换
所以我们再规定,如果说被插者和兄弟是一个人,就不能插,需要换个人
比如这里的 C 本来要插 D,但是发现 D 是自己的兄弟,那就换插 A
所以在这个用例中,C 插 A,D 也 插 A,更新两次,没毛病
至此,整个排位算法就差不多啦
具体的实现其实还是要考虑很多边界的,其实算法这东西,看实现和没意思
搞懂了本质,自己复现就很简单啦~
最后放上 fre 的 地址:https://github.com/132yse/fre
欢迎 star 与 fork ~
The text was updated successfully, but these errors were encountered: