创建react源码项目-update更新机制

137 阅读2分钟

实现更新机制

代码位置packages/react-reconciler/src/ReactFiberClassUpdateQueue.ts

创建update

react中触发更新的操作有一下几点

  • ReactDOM.createRoot(document.getElementById('root'))创建根节点也是更新操作的一环
  • setState
  • useState 在源码中update是一个包含callback属性的对象,callback是一个触发更新的属性,接收用户传入的state或者函数。这里使用action代替
export type Action<State> = State | ((prevState: State) => State);
export interface Update<State> {
   action: Action<State>;
}
export const createUpdate = <State>(action: Action<State>): Update<State> => {
    return {
        action,
    };
};

创建updateQueue

react中每个react element对应的fiberNode中都有一个updateQueue用来管理update,updateQueue结构如下

export interface UpdateQueue<State> {
    shared: {
       pending: Update<State> | null;
    };
}
export const createUpdateQueue = <State>() => {
    return {
        shared: {
            pending: null,
        },
    } as UpdateQueue<State>;
};

update放入updateQueue中

主要实现把update赋值给updateQueue.shared.pending

export const enqueueUpdate = <State>(
    updateQueue: UpdateQueue<State>,
    update: Update<State>,
) => {
    updateQueue.shared.pending = update;
};

执行update

processUpdateQueue来执行update,实现更新

export const processUpdateQueue = <State>(
    baseState: State,
    pendingState: Update<State> | null,
): {
    memoizedState: State;
} => {
    const result: ReturnType<typeof processUpdateQueue<State>> = {
            memoizedState: baseState,
    };

    if (pendingState) {
            const action = pendingState.action;
            if (action instanceof Function) {
                    result.memoizedState = action(baseState);
            } else {
                    result.memoizedState = action;
            }
    }

    return result;
};

接入更新机制

packages/react-reconciler/src/ReactFiberReconciler.ts 在update更新机制创建完成之后需要把update机制接入,react的更新是从根节点开始的,由上而下的进行的。

根节点的fiberNode需要另一个fiber节点指向它,这个最顶点的节点叫fiberRootNode

  • 顶节点:fiberRootNode
  • 根节点fiberNode: hostRootFiber

image.png

createContainer-创建获取fiberRootNode的方法

// FiberRootNode
// packages/react-reconciler/src/ReactFiber.ts
export class FiberRootNode {
    container: Container;
    current: FiberNode;
    finishedWork: FiberNode | null;

    constructor(container: Container, hostRootFiber: FiberNode) {
            this.container = container;
            this.current = hostRootFiber;
            hostRootFiber.stateNode = this;
            this.finishedWork = null;
    }
}
// // packages/react-reconciler/src/ReactFiberReconciler.ts
export const createContainer = (container: Container) => {
    const hostRootFiber = new FiberNode(HostRoot, {}, null);
    const root = new FiberRootNode(container, hostRootFiber);
    hostRootFiber.updateQueue = createUpdateQueue(); // fiberNode 添加updateQueue
    return root;
};

updateContainer - 创建容器时执行更新

// packages/react-reconciler/src/ReactFiberReconciler.ts
export const updateContainer = (
    element: ReactElement | null,
    root: FiberRootNode,
) => {
    const hostRootFiber = root.current;
    const update = createUpdate<ReactElement | null>(element);
    // 添加update
    enqueueUpdate(
        hostRootFiber.updateQueue as UpdateQueue<ReactElement | null>,
        update,
    );
    scheduleUpdateOnFiber(hostRootFiber); // 调度更新
    return element;
};

scheduleUpdateOnFiber调度更新

// packages/react-reconciler/src/ReactFiberWorkLoop.ts
export function scheduleUpdateOnFiber(fiberNode: FiberNode) {
    const root = markUpdateFromFiberToRoot(fiberNode);
    renderRoot(root);
}

markUpdateFromFiberToRoot 通过fiberNode寻找fiberRootNode

// // packages/react-reconciler/src/ReactFiberWorkLoop.ts
function markUpdateFromFiberToRoot(fiberNode: FiberNode) {
    let node = fiberNode;
    let parent = node.return;
    while (parent !== null) {
            node = parent;
            parent = node.return;
    }
    // 当前是hostRootFiber
    if (node.tag == HostRoot) {
       return node.stateNode;
    }
    return null;
}

renderRoot循环遍历fiberNode

function renderRoot(root: FiberRootNode) {
    prepareFreshStack(root);
    do {
        try {
                workLoop();
        } catch (e) {
                console.warn('workLoop出错', e);
                workInProgressRoot = null;
        }
    } while (true);
}

function prepareFreshStack(root: FiberRootNode) {
    // root.current->hostRootFiber
    workInProgressRoot = createWorkInProgress(root.current, {});
}

createWorkInProgress 获取workInProgress

createWorkInProgress中可以确定当前是挂载还是更新

export const createWorkInProgress = (
    current: FiberNode,
    pendingProps: Props,
): FiberNode => {
    let wip = current.alternate;
    if (wip === null) {
        // mount
        wip = new FiberNode(current.tag, pendingProps, current.key);
        wip.type = current.type;
        wip.stateNode = current.stateNode;

        wip.alternate = current;
        current.alternate = wip;
    } else {
        // update
        wip.pendingProps = pendingProps;
        wip.flags = NoFlags; // 初始化所有副作用
    }

    // 复用current中的部分属性
    wip.type = current.type;
    wip.updateQueue = current.updateQueue;
    wip.child = current.child;
    wip.memoizedState = current.memoizedState;
    return wip;
};