ReactDOM.render(
<App />,
document.getElementById('root')
);
这里一般是react的入口,有三个步骤。
- 将React元素转为虚拟DOM
- 创建根容器fiber
- 将元素(App)放入根fiber的更新队列
- 从当前节点找到根节点
- 从根节点进行更新
- 更新使用两个对象交替
- 进行对比后生成真实dom
- 并挂载到根节点
这里先说第一步,将React元素转为虚拟DOM,如果我们在babel输入一个JSX形式的元素,babel会调用createElement方法,这个方法会生成虚拟DOM;所以我们先来实现这个方法
<div key="1">text</div>
React.createElement("div", {
key: "1"
}, "text");
const RESOLVED_ATTR = {
_self: true,
key: true,
ref: true,
}
function createElement(type, attributes, ...children) {
let key = null;
let ref = null;
let props;
for(let propsName of attributes) {
if(propsName === key) key = attributes[propsName];
if(propsName === ref) ref = attributes[ref];
if(!RESOLVED_ATTR.hasOwnProperty(propsName)) {
props[propsName] = attributes[propsName]
}
}
if(children && children.length === 1) {
props.children = children;
} else {
props.children = []
for(let i = 0;i < children.length;i++) {
props.children.push(children[i]);
}
}
return {
$$typeof: 'REACT_ELEMENT',
type,
props
}
}
二 fiber节点 特征
-
有多个指针,比如return child sibling指针
-
一个fiber就是一个工作单元
-
fiber节点是基于虚拟DOM生成的,也是树状结构,在遍历的时候要按照一定顺序。从根节点开始工作,遍历第一个子元素,子元素开始工作,如果当前子元素没有子元素的话,当前子元素结束工作,开始兄弟元素,等到所有子元素遍历完成后,父元素完成。 开始工作:根 -》 父 -》 子 结束工作:子 -》 父亲 -》 根
-
fiber在遍历过程中会产生副作用链,父作用链会记录当前节点的操作,比如更新/创建/删除等
创建根fiber
function render(ele, container) {
let fiberRoot = createFiberRoot(container);
}
function createFiberRoot(containerInfo) {
const root = { containerInfo };
rootFiber = createHostRootFiber(); // 创建fiber对象
root.current = rootFiber;
rootFiber.stateNode = root;
initializeUpdateQueue(root); // 将root的更新对列设置为空
return root;
}
function createHostRootFiber() {
return createFiber('host');
}
function createFiber(tag, pendingProps, key) {
return new FiberNode(tag, pendingProps, key);
}
function FiberNode(tag, pendingProps, key) {
this.tag = tag;
this.pendingProps = pendingProps;
this.key = key;
}
function initializeUpdateQueue(fiber) {
const updateQueue = {
shared: {
pending: null
}
}
fiber.updateQueue = updateQueue;
}
更新根容器,即将元素放入fiber的更新队列
function render(ele, container) {
let fiberRoot = createFiberRoot(container);
updateContainer(ele, fiberRoot);
}
function updateContainer(element, container) {
const current = container.current;
const update = createUpdate(element);
update.payload = element;
enqueueUpdate(current, update);
scheduleUpdateOnFiber(current);
}
function createUpdate() {
return {};
}
// enqueueUpdate是一个循环列表
function enqueueUpdate(fiber, update) {
const updateQueue = fiber.updateQueue;
let pending = updateQueue.shared.pending;
if(!pendingProps) {
update.next = update
} else {
update.next = pending.next;
}
pendingProps = update.next;
}
从当前fiber向上找到根fiber,从根fiber执行工作循环
function scheduleUpdateOnFiber(fiber) {
const fiberRoot = markUpdateLaneFromFiberRoot(fiber); // 向上找到根fiber
performSyncWorkOnRoot(fiberRoot);
}
function markUpdateLaneFromFiberRoot(fiber) {
let node = fiber;
let parent = fiber.return;
while(parent) {
node = parent;
parent = parent.parent;
}
return node.stateNode;
}
// 从根fiber创建新的work fiber
function performSyncWorkOnRoot(root) {
workInProgressRoot = root;
workInprogress = createWorkInProgress(root.current);
}
function createWorkInProgress(current, pendingProps) {
let workInProgress = current.alternate;
if(!workInProgress) {
workInProgress = createFiber(current.tag, pendingProps);
workInProgress.child = null;
workInProgress.stateNode = current.stateNode;
workInProgress.alternate = current;
current.alternate = workInProgress;
} else {
workInProgress.pendingProps = pendingProps;
}
workInProgress.flag = 0;
workInProgress.child = null;
workInProgress.sibling = null;
workInProgress.updateQueue = current.updateQueue;
return workInProgress;
}
function performSyncWorkOnRoot(root) {
workInProgressRoot = root;
workInprogress = createWorkInProgress(root.current);
// 执行工作循环, 构建副作用链
workLoopSync(workInprogress);
}
function workLoopSync(workInProgress) {
while(workInProgress) {
performUnitOfWork(workInProgress);
}
}
function performUnitOfWork(workInProgress) {
const current = workInProgress.alternate;
// 构建新的子fiber并返回
let next = beginWork(current, workInProgress);
if(next) {
workInProgress = next;
} else {
computedUnitOfWork(current, workInProgress);
}
}
function beginWork(current, workInProgress) {
const type = workInProgress.tag;
switch(type) {
case 'host':
return updateHostRoot(current, workInProgress);
case 'component':
return updateHostComponent(current, workInProgress);
default:
return;
}
}
function updateHostRoot(current, workInProgress) {
let nextChild = workInProgress.updateQueue.shared.pending.element;
// 根据老的fiber和新的虚拟dom,创建新的子fiber;
reconcileChildren(current, workInProgress, nextChild);
return workInProgress.child;
}
// 创建并返回第一个新的子fiber
function reconcileChildFibers(returnFiber, currentFirstChild, newChild) {
if( nextChild && typeof nextChild === 'object') {
switch(newChild.$$typeof) {
case 'REACT_ELEMENT_TYPE':
// placeSingleChild是根据current属性,为当前fiber添加副作用属性;
// reconcileSingleFiber是根据虚拟dom,创建子fiber;
return placeSingleChild(reconcileSingleFiber(returnFiber, currentFirstChild, newChild));
}
let childFiber = createFiber();
childFiber.return = workInProgress;
childFiber.flags = '1';
}
}
function reconcileSingleFiber(returnFiber, currentFirstChild, newChild) {
const create = createFiberFromElement(newChild);
create.return = returnFiber;
return create;
}
当新的子fiber构建完成后,生成新的dom节点,挂载到fiber上,并把当前副作用链接到父作用链上
function performUnitOfWork(workInProgress) {
const current = workInProgress.alternate;
// beginwork从上到下
let next = beginWork(current, workInProgress);
if(next) {
workInProgress = next;
} else {
// computedUnitOfWork从下到上
// 根据子fiber创建真实节点,并提交自己的副作用
computedUnitOfWork(workInProgress);
}
}
function computedUnitOfWork(unitOfWork) {
let completedWork = unitOfWork;
do{
const current = completedWork.alternate;
const returnedFiber = completedWork.return;
// 完成此fiber对应的真实dom节点的创建
completeWork(current, completedWork);
// 收集当前fiber的副作用到父fiber
collectEffect(returnedFiber, completedWork);
// 当自己这个fiber完成后,寻找下一个要构建的fiber
const siblingFiber = completedWork.sibling;
if(siblingFiber) {
workInProgress = siblingFiber;
return;
}
completedWork = returnedFiber;
workInProgress = completedWork;
} while(workInProgress);
}
function completeWork(current, workInProgress) {
const newProps = workInProgress.pendingProps;
switch(workInProgress.tag) {
case 'hostComponent':
// buildComponent,通过document.createElement创建dom
const instance = buildComponent(type, workInProgress);
workInProgress.stateNode = instance;
// finalizeInitialChildren为新的dom添加属性
finalizeInitialChildren(instance, type, newProps);
}
}
// 把自己到副作用提交到父节点上,最终返回一条作用链
function collectEffect(returnedFiber, completedWork) {
if(!returnedFiber.firstEffect) {
returnedFiber.firstEffect = completedWork.firstEffect;
}
if(completedWork.lastEffect) {
if(returnedFiber.lastEffect) {
returnedFiber.lastEffect.nextEffect = completedWork.firstEffect;
}
returnedFiber.lastEffect = completedWork.lastEffect;
}
else {
returnedFiber.lastEffect.next = completedWork.firstEffect;
}
const flags = completedWork.flags;
if(flags) {
if(returnedFiber.lastEffect) {
returnedFiber.lastEffect.nextEffect = completedWork;
} else {
returnedFiber.firstEffect = completedWork;
}
returnedFiber.lastEffect = completedWork;
}
}
构建完副作用链后,进行提交,也就是根据副作用来操作dom
function performSyncWorkOnRoot(root) {
workInProgressRoot = root;
workInprogress = createWorkInProgress(root.current);
// 执行工作循环, 构建副作用链
workLoopSync(workInprogress);
// 提交
commitRoot();
}
function commitRoot() {
const finishedWork = workInProgressRoot.current.alternate;
workInProgressRoot.finishedWork = finishedWork;
commitMutationEffects(workInProgressRoot);
}
// 生成新的dom树,并将current指向新的fiber
function commitMutationEffects(root) {
const finishedWork = root.finishedWork;
let nextEffect = finishedWork.nextEffect;
while(nextEffect) {
const flags = nextFiber.flags;
// 添加新的元素
if(flags === 2) {
const stateNode = nextEffect.stateNode;
const parentStateNode = nextEffect.return.stateNode;
parentStateNode.appendChild(stateNode);
}
nextEffect = nextEffect.nextEffect;
}
root.current = finishedWork;
}
更新
- 在render的时候,判断根节点是否创建,如果创建了就复用,没有的话新建
function render(ele, container) {
let fiberRoot = container.__reactRootContainer;
if(!fiberRoot) fiberRoot = container.__reactRootContainer = createFiberRoot(container);
updateContainer(ele, fiberRoot);
}
// 在当前的fiber中,添加更新队列
function updateContainer(element, container) {
const current = container.current;
const update = createUpdate(element);
update.payload = element;
enqueueUpdate(current, update);
scheduleUpdateOnFiber(current);
}
// 调度更新,也就是创建新的fiber
function scheduleUpdateOnFiber(fiber) {
const fiberRoot = markUpdateLaneFromFiberRoot(fiber);
performSyncWorkOnRoot(fiberRoot);
}
function performSyncWorkOnRoot(root) {
workInProgressRoot = root;
workInprogress = createWorkInProgress(root.current);
// 执行工作循环, 构建副作用链
workLoopSync(workInprogress);
// 提交
commitRoot();
}
这里属于开始阶段
这里和之前初次渲染时不一样,如果不是初次渲染,并且只生成了一个虚拟DOM,需要对比之前fiber的key和type,来决定是更新还是删除
function reconcileSingleFiber(returnFiber, currentFirstChild, element) {
let key = element.key;
let child = currentFirstChild;
while(child) {
if(child.key === key) {
if(child.type === element.type) {
deleteAllChild(returnFiber, child.sibling);
// 复用老的节点和新的属性,返回更新后的fiber
const existing = useFiber(child, element.props);
existing.return = returnFiber;
return existing;
} else {
// 所有都不能复用
deleteAllChild(returnFiber, child);
break;
}
} else {
// 当前fiber不能复用
deleteChild(returnFiber, child);
}
child = child.sibling;
}
// 如果不能复用的话,构建新的fiber,并返回
const create = createFiberFromElement(newChild);
create.return = returnFiber;
return create;
}
function useFiber(oldFiber, props) {
return createWorkInProgress(oldFiber, props);
}
function deleteAllChild(returnFiber, child) {
while(child) {
deleteChild(returnFiber, child);
child = child.sibling;
}
}
// 标记为删除,并在父节点上添加副作用,目的是放在链表的头部
function deleteChild(returnedFiber, child) {
child.nextEffect = null;
child.flags = '3';
let lastEffect = returnedFiber.lastEffect;
if(lastEffect) {
returnedFiber.nextEffect = child;
returnedFiber.lastEffect = child;
} else {
returnFiber.firstEffect = returnedFiber.lastEffect = child;
}
}
这里属于完成阶段
function completeWork(current, workInProgress) {
const newProps = workInProgress.pendingProps;
switch(workInProgress.tag) {
case 'hostComponent':
// 节点可复用
if(current && workInProgress.stateNode) {
// 复用节点,更新属性
updateHostComponent(current, workInProgress, workInProgress.tag, newProps);
}
const instance = buildComponent(current, workInProgress);
workInProgress.stateNode = instance;
finalizeInitialChildren(instance, type, newProps);
}
}
// 更新属性
function updateHostComponent(current, workInProgress, tag, newProps) {
let oldProps = current.memoizedProps;
let instance = workInProgress.stateNode;
// 返回属性数组
const updatePayload = diffProperties(instance, tag, oldProps, newProps);
// 把更新数组添加到更新队列中,提交阶段可以拿来用
// 根fiber的更新队列和原生fiber的更新队列不一样,原生fiber [key1,value1, key2, value2 ],根fiber是一个循环链表
workInProgress.updateQueue = updatePayload;
if(updatePayload) {
workInProgress.flags |= 0;
}
}
// 返回更新的属性数组
function diffProperties(domElement, tag, lastProps, newProps) {
let updatePayload = null;
let propKey;
for(propKey in lastProps) {
if(lastProps[propKey] && !newProps[propKey]) {
(updatePayload = updatePayload || []).push(propKey, null);
}
}
for(propKey in newProps) {
if(propKey === 'children') {
if(typeof newProps[propKey] === 'string' || typeof newProps[propKey] === 'number') {
if(newProps[propKey] !== oldProps[propKey]) {
(updatePayload = updatePayload || []).push(propKey, newProps[propKey]);
}
}
} else {
if(newProps[propKey] !== lastProps[propKey]) {
(updatePayload = updatePayload || []).push(propKey, newProps[propKey]);
}
}
}
return updatePayload;
}
提交阶段
// 从根节点进行操作,看是否有副作用,
function commitMutationEffects(root) {
const finishedWork = root.finishedWork;
let nextEffect = finishedWork.nextEffect;
while(nextEffect) {
const flags = nextFiber.flags;
let current = nextEffect.alternate;
// 添加
if(flags === 2) {
const stateNode = nextEffect.stateNode;
const parentStateNode = nextEffect.return.stateNode;
parentStateNode.appendChild(stateNode);
} else if(flags === 1) {
// 删除
commitDeletion(nextEffect);
} else if(flags === 3) {
// 更新
commitWork(current, nextEffect);
}
nextEffect = nextEffect.nextEffect;
}
root.current = finishedWork;
}
// 更新dom操作
function commitWork(current, finishedWork) {
// 获取属性更新数组
const updatePayload = finishedWork.updateQueue;
finishedWork.updateQueue = null;
if(updatePayload) {
updateProperties(finishedWork.stateNode, updatePayload);
}
}
function updateProperties(domElement, updatePayload) {
for(let i = 0;i < updatePayload.length;i+=2) {
let propKey = updatePayload[i];
let propVal = updatePayload[i + 1];
if(propKey === 'children') {
domElement.textContent = propVal;
} else domElement.setAttribute(propKey, propVal);
}
}
// 删除dom操作
function commitDeletion(fiber) {
if(!fiber) return;
let parentStateNode = getParentStateNode(fiber);
parentStateNode.removeChild(fiber.stateNode);
}