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
fre 是一个 react-like 框架,代码不多,总体上就一个 fiber 结构和一个 reconcile 算法
fiber 结构本质上是使用一个三个指针的链表来描述 dom 结构,在这个链表的基础上进行深度遍历,目的是为了做异步渲染(时间切片,suspense)
let root = fiber let node = fiber while (true) { if (node.child) { node = node.child continue } if (node === root) return while (!node.sibling) { if (!node.parent || node.parent=== root) return node = node.parent } node = node.sibling }
传统的 diff 算法,都是基于数组的同步 diff 算法,拿 vue3、inferno 为例,本质上是寻找一个“最长公共子序列”,然后保持不同,剩下的进行移动操作,而最长公共子序列可以转换为最长递增子序列问题,进而使用二分查找将复杂度优化到O(nlogn)
聪明的人已经发现了,传统的基于 LCS/LIS 的算法,是无法用到这个结构里的,甚至同样的思路都不适用于链表,因为链表是个线性结构,它的遍历限制了算法的发挥(只能从头到尾 onepass)
因为我不得不思考一个新的适用于链表的 onepass 的 reconcile 算法
我尝试过各种算法以后,想到一种方法,既然我们没办法在链表中寻找最长公共子序列,那么我们就寻找最长移动节点
[1, 2, 3, 4, 5] => [5, 1, 2, 3, 4] // 5 从 4 到了 0,明显的最大移动距离,因为我们就只移动 5
这个思路可能不能总是寻找到最短编辑距离,但大多数时候可以保持 onepass,可以和 fiber 结构结合起来,最好复杂度甚至可以做到 O(n)
function reconcile(bList, view, x) { let currentA = aList.head let currentB = bList.head let position = 0 while (currentA || currentB) { if (!currentB) { // 删除多余节点 removeNode(currentA) currentA = currentA.next continue; } if (!currentA) { // 插入新节点 insertNode(currentB, null) currentB = currentB.next continue } const bKey = currentB.data.key const aKey = currentA.data.key if (aKey === bKey) { // 更新节点 this.updateNode(currentA, currentB, view) currentA = currentA.next currentB = currentB.next } else { // 寻找最长距离 let foundA = this.findKeyInRemaining(aList, currentA.next, bKey) let foundB = this.findKeyInRemaining(bList, currentB.next, aKey) if (foundA && foundB) { // 比较移动距离 if (foundA.distance <= foundB.distance) { // 移动A链表节点 this.moveNodeBefore(foundA.node, currentA) currentA = foundA.node; } else { // 移动B链表节点 this.insertNode(foundB.node, currentA) currentB = foundB.node.next } } else if (foundA) { // 移动A链表节点 this.moveNodeBefore(foundA.node, currentA); currentA = foundA.node } else { // 新增B链表节点 this.insertNode(currentB, currentA) currentB = currentB.next } } position++; } }
fre 的意义在于使用 500 行代码复刻 react 的核心结构和异步渲染的思路,供大家参考
最近考研复试实在太紧张了,工作也好,面试也好,学术研究也好,因为俺的思路也能帮到大家,如果大家有更好的思路也可以发俺哈
The text was updated successfully, but these errors were encountered:
No branches or pull requests
fre 是一个 react-like 框架,代码不多,总体上就一个 fiber 结构和一个 reconcile 算法
fiber 结构
fiber 结构本质上是使用一个三个指针的链表来描述 dom 结构,在这个链表的基础上进行深度遍历,目的是为了做异步渲染(时间切片,suspense)
传统的 diff 算法,都是基于数组的同步 diff 算法,拿 vue3、inferno 为例,本质上是寻找一个“最长公共子序列”,然后保持不同,剩下的进行移动操作,而最长公共子序列可以转换为最长递增子序列问题,进而使用二分查找将复杂度优化到O(nlogn)
聪明的人已经发现了,传统的基于 LCS/LIS 的算法,是无法用到这个结构里的,甚至同样的思路都不适用于链表,因为链表是个线性结构,它的遍历限制了算法的发挥(只能从头到尾 onepass)
因为我不得不思考一个新的适用于链表的 onepass 的 reconcile 算法
在 fiber 中实现最长移动
我尝试过各种算法以后,想到一种方法,既然我们没办法在链表中寻找最长公共子序列,那么我们就寻找最长移动节点
这个思路可能不能总是寻找到最短编辑距离,但大多数时候可以保持 onepass,可以和 fiber 结构结合起来,最好复杂度甚至可以做到 O(n)
总结
fre 的意义在于使用 500 行代码复刻 react 的核心结构和异步渲染的思路,供大家参考
最近考研复试实在太紧张了,工作也好,面试也好,学术研究也好,因为俺的思路也能帮到大家,如果大家有更好的思路也可以发俺哈
The text was updated successfully, but these errors were encountered: