Understanding React, Vue2, and Vue3 Diff Algorithms and Why Vue Does Not Use Fiber
This article explains React's Diff algorithm and Fiber architecture, compares it with Vue2 and Vue3's double‑ended diff approaches, and discusses why Vue does not need Fiber or time‑slicing, providing code examples and visual illustrations of the underlying mechanisms.
Preface
Everyone talks about the "double‑ended diff algorithm". What exactly is it, and how does it differ from React's Diff algorithm?
To answer this, we first examine React's Diff algorithm, then Vue3's, and finally Vue2's, before comparing their differences.
We also explain why Vue does not need to use a Fiber architecture.
React Official Explanation
React's source comments already explain why it does not adopt Vue's double‑ended diff algorithm. Below is the relevant code snippet from React's source:
function reconcileChildrenArray(
returnFiber: Fiber,
currentFirstChild: Fiber | null,
newChildren: Array<*>,
expirationTime: ExpirationTime,
): Fiber | null {
// This algorithm can't optimize by searching from both ends since we
// don't have backpointers on fibers. I'm trying to see how far we can get
// with that model. If it ends up not being worth the tradeoffs, we can
// add it later.
// ... (omitted for brevity) ...
}The gist is that React cannot use a double‑ended diff optimization because the Fiber data structure lacks backward links, and the team is still evaluating whether the added complexity is worthwhile.
Even if a double‑ended algorithm were used, React would still need a Map to handle cases where many changes occur, falling back to a forward‑only scan initially and switching to a map‑based approach when necessary.
Fiber Structure
Before React 16, the virtual DOM creation and Diff process were non‑interruptible, causing long JavaScript execution times that could block rendering. To avoid UI jank, React 16 introduced the Fiber architecture, turning the virtual DOM tree into a linked list of Fiber nodes that can be paused and resumed.
A Fiber node contains static properties (e.g., tag , key , elementType ) and dynamic work‑unit properties (e.g., pendingProps , memoizedState , effectTag ). The core linking fields are return (parent), child (first child), and sibling (next sibling).
function FiberNode(tag, pendingProps, key, mode) {
// static fields
this.tag = tag;
this.key = key;
this.elementType = null;
this.type = null;
this.stateNode = null;
// linking fields
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
// dynamic fields
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null;
}Because each Fiber knows its parent, first child, and next sibling, but not its previous sibling, the structure is a one‑directional linked list, which is why React cannot easily perform a true double‑ended traversal.
Generation of the Fiber Linked List
After JSX compilation, the initial virtual DOM is turned into a root FiberRoot node. The reconciler then walks from this root, creating a workInProgress Fiber tree . The simplified code for creating child Fibers looks like this: export function reconcileChildren(returnFiber, children) { if (isStringOrNumber(children)) { return; } const newChildren = isArray(children) ? children : [children]; let previousNewFiber = null; let shouldTrackSideEffects = !!returnFiber.alternate; let oldFiber = returnFiber.alternate && returnFiber.alternate.child; let lastPlacedIndex = 0; let newIdx = 0; if (!oldFiber) { for (; newIdx < newChildren.length; newIdx++) { const newChild = newChildren[newIdx]; if (newChild === null) continue; const newFiber = createFiber(newChild, returnFiber); lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx, shouldTrackSideEffects); if (previousNewFiber === null) { returnFiber.child = newFiber; } else { previousNewFiber.sibling = newFiber; } previousNewFiber = newFiber; } return; } } The completed workInProgress Fiber tree is later committed to the DOM during the commit phase. React Diff Algorithm The algorithm performs a depth‑first traversal: it visits children first, then siblings, then uncles, and finally climbs up the tree until all updates are processed. During reconciliation, the parent Fiber coordinates the diff of its children. The new virtual DOM array is scanned from left to right, trying to reuse existing Fibers when possible. If a match is found, a new Fiber is created that reuses the old DOM node and is marked with an Update flag. After the first pass, any remaining old Fibers are placed into a Map keyed by their key (or index). The second pass uses this map to find matches for the remaining new nodes. Unmatched new nodes receive a Placement flag (creation), while matched nodes may receive Placement if they need to move (i.e., their old index is less than the last placed index). Key points: First pass: left‑to‑right comparison, stop when a mismatch occurs. If all new nodes are matched, delete any leftover old nodes. Second pass: build a map of remaining old Fibers, then try to match each new node; unmatched new nodes get Placement . After all new nodes are processed, delete any old Fibers that were never matched. Illustrated Explanation of React Diff Algorithm Simple scenario: new virtual DOM matches old Fibers A → B → C one‑by‑one, so no moves are needed and only leftover old nodes are deleted. Complex scenario: when a new node cannot be matched in the first pass, the algorithm falls back to the map‑based second pass, handling moves, insertions, and deletions as described above. The article includes several images (omitted here) that visualize these steps. Vue3 Diff Algorithm Vue3 uses a double‑ended diff for arrays. The first round compares from both ends until a mismatch is found, narrowing the problem to a middle segment. If the new list finishes first, remaining old nodes are deleted; if the old list finishes first, remaining new nodes are created. The second round builds a key → index map for the remaining new nodes and an array newIndexToOldIndexMap for the old nodes. It then finds the longest increasing subsequence (LIS) to identify stable nodes that do not need to move. Nodes not in the LIS are either created or moved. Vue2 Diff Algorithm Vue2 also relies on a double‑ended approach but with four fast‑path checks: head‑to‑head, tail‑to‑tail, head‑to‑tail, and tail‑to‑head. If none match, it falls back to a full scan. After the fast passes, the algorithm processes remaining nodes similarly to Vue3, using a map for key‑based lookup and handling insertions, deletions, and moves. Comparison of React, Vue3, Vue2 Diff Algorithms Similarities All three frameworks first handle simple cases before tackling complex ones, and they all rely on a key → Fiber map for fast look‑ups. They also use insertBefore(newNode, existingNode) for DOM insertion. Differences Vue can analyze static nodes at compile time (template‑based) and either skip them (Vue2) or hoist them (Vue3), while React (JSX‑based) cannot perform such static analysis. Vue2 and Vue3 perform diff and update synchronously, similar to React 15, whereas React 16+ performs diff asynchronously using the Fiber architecture, allowing interruption of long tasks. Both Vue2 and Vue3 use double‑ended diff; React's single‑directional Fiber list prevents a true double‑ended traversal unless a reverse link is added. Why Vue Does Not Need Fiber Vue does not require time‑slicing because its reactive dependency tracking limits updates to the minimal component subtree, and its template compiler can perform static‑node optimizations. React, lacking these compile‑time insights, introduced Fiber and time‑slicing to avoid long, blocking updates. Empirical studies show that humans do not notice delays under 100 ms, so Vue's typical update cost rarely exceeds this threshold, making Fiber‑style time‑slicing unnecessary. Conclusion This article started with the question "Why does React's Diff algorithm not adopt Vue's double‑ended diff?" and explored related concepts such as Fiber, the generation of the Fiber linked list, and a detailed walkthrough of React's Diff algorithm with diagrams. It then gave brief overviews of Vue3 and Vue2 diff algorithms, compared the three approaches, and finally summarized why Vue does not need Fiber or time‑slicing. I am currently participating in the Juejin creator signing program recruitment activity. Click here to apply . Reference [1] https://juejin.cn/post/7112770927082864653
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.