likes
comments
collection
share

React链表结构的核心概念

作者站长头像
站长
· 阅读数 17

React链表结构的核心概念

1. Fiber

React链表结构的核心概念

  • 结构上,是一个对象
  • 描述了一个React元素或Dom节点
  • 新的协调引擎,主要目的是使Virtual DOM可以进行增量式渲染
export type Fiber = {
    // 标记组件类型,如函数组件、类组件、原生便签等
    tag: WorkTag,
    
    // 标记节点的唯一性,diff时候判断节点是否能复用的依据之一,其他两个是同一层级、类型相同
    key: null | string,
    
    // 保存节点在协调阶段的element.type的值
    elementType: any,
    
    // fiber的type
    type: any,
    
    // 不同类型的组件,stateNode代表的值不同:
    // 原生标签,stateNode是dom节点
    // 类组件,stateNode是实例
    stateNode: any,
    
    // 父fiber
    return: Fiber | null,
    
    // fiber children是单链表结构,这也是React VDOM DIFF算法中单向查找的原因(Vue VDOM DIFF中是双向查找)
    // 第一个子fiber
    child: Fiber | null,
    // 下一个兄弟fiber
    sibling: Fiber | null,
    // 标记此fiber在当前层级下的位置下标
    index: number,
    
    // 节点的ref
    ref: null 
        | (((handle: mixed) => void) & {_stringRef: ?sting, ...}}
        | RefObject,
    
    // 还未更新完成组件的props
    pendingProps: any,
    // 上次组件更新的props
    memoizedProps: any,
    
    // update的queue, update里包括了状态值、callback等
    // update是个单向循环链表
    updateQueue: mixed,
    
    // 类组件中存的是状态值
    // 函数组件中存的是第0个hook, hooks也是单链表
    memoizedState: any,
    
    // 如果该组件消费了context value, 这里存的就是context对象
    dependencies: Dependencies | null
    
    // 如concurrent模式
    mode: TypeOfMode,
    
    // 标记fiber的effect, 如Placement(末尾插入appendChild、向前插入insertBefore)、Update(更新)等
    flags: Flags,
    // 子树的flags
    subtreeFlags: Flags,
    // 如果有要删除的子fiber, 则存在这个数组中
    deletions: Array<Fiber> | null
    
    // 标记节点的lanes
    lanes: Lanes,
    childLanes: Lanes,
    
    // 上一个fiber
    alternate: Fiber | null
}

2. Hook

  • 解决类组件难以解决的问题,比如状态复用、组件臃肿等
  • 每次函数组件执行的时候,hook都会再次执行;Vue Composition只是首次执行一次
  • 结构上,为单链表,hook顺序代表链表的唯一性,所以引用hook不能使用条件、循环等语句
export type Hook = {
    // 如果是useState或者useReducer, 这个memoizedState记录的是状态值
    // 如果是useEffect或useLayoutEffect,  这个memoizedState记录的是effect
    // 如果是useMemo,  这个memoizedState记录的是新值和依赖数组,即[nextValue, nextDeps]
    // 如果是useDeferredValue,  这个memoizedState记录的是这个value
    memoizedState: any,
    baseState: any,
    baseQueue: Update<any, any> | null,
    queue: any,
    // 下一个hook, 单链表结构
    next: Hook | null
}

3. Update

  • 初次渲染、setState、forceUpdate的更新
  • 通过批量更新实现的异步更新
function enqueueUpdate<S, A>(
    fiber: Fiber,
    queue: UpdateQueue<S, A>,
    update: Update<S, A>,
    lane: Lane
) {
    ...
}

4. Effect

  • useEffect和useLayoutEffect处理effect

函数组件初次渲染

  • 记录effect和deps
  • pushEffect函数,把effect单向循环链表,存储在fiber.updateQueue.lastEffect上
// effect
function mountEffect(
    create: () => (() => void) | void,
    deps: Array<mixed> | void | null
):void {
    return mountEffectImpl(
        PassiveEffect | PassiveStaticEffect,
        HookPassive,
        create,
        deps
    )
}

function mountEffectImpl(fiberFlags, hookFlags, create, deps): void {
    const hook = mountWorkInProgressHook();
    const nextDeps = deps === undefined ? null : deps;
    currentlyRenderingFiber.flags = fiberFlags;
    hook.memoizedState = pushEffect(
        HookHasEffect | hookFlags,
        create,
        undefined,
        nextDeps
    )
}

函数组件更新阶段

  • 相对于初次渲染阶段,更新阶段的不同之处在于多了对deps的对比,如果组件更新前后,依赖项不变,则后续的effect并不会添加到updateQueue中,也就意味着create函数不会执行。
  • 并且,初次渲染阶段不会记录destory函数,只需函数组件更新阶段记录到effect中。
  • 在记录阶段,effect都是记录到fiber.updateQueue中

5. 总结

  • 单向链表fiber、hook
  • 单向循环链表update、effect
转载自:https://juejin.cn/post/7281825352531640331
评论
请登录