「06」接入状态更新机制

337 阅读3分钟

在前面已经实现了Update的基本架构,接下来的工作包括:

  • 实现mount时调用的API
  • 将该API接入上述Update更新机制中

目前需要考虑的事情:

  • 更新可能发生于任意组件,而更新流程是从根节点递归的
  • 需要一个统一的根节点保存通用信息
ReactDOM.createRoot(rootElement).render(<App/>)

对应到源码内部,执行ReactDOM.createRoot会生成一个fiberRootNode,然后传进来的rootElement这个DOM也有对应的节点即hostRootFiber,执行render方法,传进去App组件,就挂载到hostRootFiber

实现fiberRootNode

  • container保存对应宿主环境挂载的节点,暂时定义一个描述宿主环境对应方法的文件hostConfig.ts

react-reconciler/src/hostConfig.ts

export type Container = any;

react-reconciler/src/fiber.ts

fiberRootNode的current指针指向hostRootFiber
finishiedWork指向已经更新完成的整个hostRootFiber

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;
	}
}

实现mount时调用的api

新建react-reconciler/src/fiberReconciler.ts对外暴露两个函数分别是:createContainerupdateContainer

export function createContainer(container:Container){}

export function updateContainer(container:Container){}

实现createContainer
这个方法可以初始化一个hostRootFiberFiberRootNode,并且调用createUpdateQueue就能够实现fiberNode与更新机制的关联

export function createContainer(container: Container) {
	const hostRootFiber = new FiberNode(HostRoot, {}, null);
	const root = new FiberRootNode(container, hostRootFiber);
	hostRootFiber.updateQueue = createUpdateQueue();
	return root;
}

实现updateContainer
createUpdate传入element就表明更新与这个element相关,之后的更新就会对这个element进行

export function updateContainer(
	element: ReactElementType | null,
	root: FiberRootNode
) {
	const hostRootFiber = root.current;
	const update = createUpdate<ReactElementType | null>(element);
	enqueueUpdate(
		hostRootFiber.updateQueue as UpdateQueue<ReactElementType | null>,
		update
	);
	scheduleUpdateOnFiber(hostRootFiber);
	return element;
}

完整代码

import { Container } from 'hostConfig';
import { ReactElementType } from 'shared/ReactTypes';
import { FiberNode, FiberRootNode } from './fiber';
import {
	createUpdate,
	createUpdateQueue,
	enqueueUpdate,
	UpdateQueue
} from './updateQueue';
import { scheduleUpdateOnFiber } from './workLoop';
import { HostRoot } from './workTags';

export function createContainer(container: Container) {
	const hostRootFiber = new FiberNode(HostRoot, {}, null);
	const root = new FiberRootNode(container, hostRootFiber);
	hostRootFiber.updateQueue = createUpdateQueue();
	return root;
}

export function updateContainer(
	element: ReactElementType | null,
	root: FiberRootNode
) {
	const hostRootFiber = root.current;
	const update = createUpdate<ReactElementType | null>(element);
	enqueueUpdate(
		hostRootFiber.updateQueue as UpdateQueue<ReactElementType | null>,
		update
	);
	scheduleUpdateOnFiber(hostRootFiber);
	return element;
}

如何让updateContainer与renderRoot更新流程串联上

workLoop中实现scheduleUpdateOnFiber,即在fiber中调度update

export function scheduleUpdateOnFiber(fiber: FiberNode) {
	// TODO 调度功能
	// fiberRootNode
	const root = markUpdateFromFiberToRoot(fiber);
	renderRoot(root);
}

从当前节点遍历到root节点

// 从当前节点一直遍历到根节点
function markUpdateFromFiberToRoot(fiber: FiberNode) {
	let node = fiber;
	let parent = node.return;
	// 普通的fiberNode
	while (parent !== null) {
		node = parent;
		parent = node.return;
	}
	// hostRootFiber的时候
	if (node.tag === HostRoot) {
		return node.stateNode;
	}
	return null;
}

实现createWorkInProgress

export const createWorkInProgress = (
	current: FiberNode,
	pendingProps: Props
): FiberNode => {
	let wip = current.alternate;

	// 首屏渲染wip===null
	if (wip === null) {
		// mount
		wip = new FiberNode(current.tag, pendingProps, current.key);
		wip.stateNode = current.stateNode;

		wip.alternate = current;
		current.alternate = wip;
	} else {
		// update
		wip.pendingProps = pendingProps;
		// 副作用清除掉 可能是上次遗留的
		wip.flags = NoFlags;
	}
	wip.type = current.type;
	wip.updateQueue = current.updateQueue;
	wip.child = current.child;
	wip.memoizedProps = current.memoizedProps;
	wip.memoizedState = current.memoizedState;

	return wip;
};

整体流程

  • createContainer创建应用的根节点fiberRootNode,并将fiberRootNodehostRootFiber连接起来
  • updateContainer创建update并将update添加入updateQueue中,将首屏渲染与触发更新的机制连接了起来

触发更新机制包含

  • createUpdate更新对应的数据结构update
  • createUpdateQueue保存update的结构updateQueue
  • equeueUpdate将update插入到updatequeue中
  • processUpdateQueue基于一个基础的状态以及pendingUpdate消费update,经过计算得到最终的状态memoizedState

fiberReconciler中

当将update插入到updatequeue中之后执行scheduleUpdateOnFiber开始调度流程

workLoop中

  • scheduleUpdateOnFiber从当前节点一直遍历到fiberRootNode,接着执行renderRoot
  • renderRoot首先根据fiberRootNode的current生成workInProgressFiber接着开始更新流程

所有代码:github.com/ohlyf/oh-re…