示例代码
import React, { useState } from 'react'
import logo from './logo.svg'
import './App.css'
const ChildCom = () => {
return <ul style={{height: '40px', overflow: 'auto', border: '1px solid red'}}>
<li>1</li>
<li>2</li>
<li>{new Date().getTime()}</li>
<p>4</p>
</ul>
}
function App() {
let [count, setCount] = useState(0)
const ChildCom2 = () => {
return <ul style={{height: '40px', overflow: 'auto', border: '1px solid red'}}>
<li>1</li>
<li>2</li>
<li>{new Date().getTime()}</li>
<li>4</li>
</ul>
}
return (
<div>
<ChildCom2/>
{/* 先插入这里,在对比,这时候比较的是两个ul */}
{ChildCom2()}
{/* <ChildCom/> */}
<button onClick={() => setCount(count+1)}>change count</button>
</div>
)
}
ReactDOM.render(
<App/> ,
document.getElementById('root')
)
babel转译之后
import React, { useState } from "react";
import logo from "./logo.svg";
import "./App.css";
const ChildCom = () => {
return /*#__PURE__*/ React.createElement(
"ul",
{
style: {
height: "40px",
overflow: "auto",
border: "1px solid red"
}
},
/*#__PURE__*/ React.createElement("li", null, "1"),
/*#__PURE__*/ React.createElement("li", null, "2"),
/*#__PURE__*/ React.createElement("li", null, new Date().getTime()),
/*#__PURE__*/ React.createElement("p", null, "4")
);
};
function App() {
let [count, setCount] = useState(0);
const ChildCom2 = () => {
return /*#__PURE__*/ React.createElement(
"ul",
{
style: {
height: "40px",
overflow: "auto",
border: "1px solid red"
}
},
/*#__PURE__*/ React.createElement("li", null, "1"),
/*#__PURE__*/ React.createElement("li", null, "2"),
/*#__PURE__*/ React.createElement("li", null, new Date().getTime()),
/*#__PURE__*/ React.createElement("li", null, "4")
);
};
return /*#__PURE__*/ React.createElement(
"div",
null,
/*#__PURE__*/ React.createElement(ChildCom2, null),
ChildCom2(),
/*#__PURE__*/ React.createElement(
"button",
{
onClick: () => setCount(count + 1)
},
"change count"
)
);
}
ReactDOM.render(
/*#__PURE__*/ React.createElement(App, null),
document.getElementById("root")
);
我们从div这一层级开始遍历
workLoopSync
performUnitOfWork
beginWork
function beginWork(current, workInProgress, renderLanes) {
switch (workInProgress.tag) {
...
case HostComponent:
return updateHostComponent(current, workInProgress, renderLanes);
...
}
}
updateHostComponent
这里nextChildren拿到的是当前fiber对应的虚拟dom中的子元素虚拟dom,结构如下
这里传到后面的nextChildren中的每一项相当于是newFiber
function updateHostComponent(current, workInProgress, renderLanes) {
...
var nextProps = workInProgress.pendingProps;
var nextChildren = nextProps.children;
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
return workInProgress.child;
}
reconcileChildren
function reconcileChildren(current, workInProgress, nextChildren, renderLanes) {
if (current === null) {
// If this is a fresh new component that hasn't been rendered yet, we
// won't update its child set by applying minimal side-effects. Instead,
// we will add them all to the child before it gets rendered. That means
// we can optimize this reconciliation pass by not tracking side-effects.
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes);
} else {
// If the current child is the same as the work in progress, it means that
// we haven't yet started any work on these children. Therefore, we use
// the clone algorithm to create a copy of all the current children.
// If we had any progressed work already, that is invalid at this point so
// let's throw it out.
workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderLanes);
}
}
reconcileChildFibers
对fiber节点处理,下面说的主要涉及下面几个函数
var reconcileChildFibers = ChildReconciler(true);
var mountChildFibers = ChildReconciler(false);
function ChildReconciler(shouldTrackSideEffects) {
function deleteChild(returnFiber, childToDelete) {}
function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, lanes) {}
function updateSlot(returnFiber, oldFiber, newChild, lanes) {}
function updateElement(returnFiber, current, element, lanes) {}
...其它
function reconcileChildFibers(){}
return reconcileChildFibers
}
reconcileChildrenArray
updateSlot生成新的newFiber,
deleteChild删除了oldFiber,
placeChild对新的fiber做标记
function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, lanes) {
var oldFiber = currentFirstChild;
for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
var newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], lanes);
if (shouldTrackSideEffects) {
if (oldFiber && newFiber.alternate === null) {
// We matched the slot, but we didn't reuse the existing fiber, so we
// need to delete the existing child.
deleteChild(returnFiber, oldFiber);
}
}
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
}
updateSlot
将newChildren中的每一项和oldFiber做对比,看是否需要重新生成Fiber
function updateSlot(returnFiber, oldFiber, newChild, lanes) {
var key = oldFiber !== null ? oldFiber.key : null;
if (typeof newChild === 'string' || typeof newChild === 'number') {
if (key !== null) {
return null;
}
return updateTextNode(returnFiber, oldFiber, '' + newChild, lanes);
}
if (typeof newChild === 'object' && newChild !== null) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE:
{
if (newChild.key === key) {
return updateElement(returnFiber, oldFiber, newChild, lanes);
} else {
return null;
}
}
}
}
return null;
}
updateElement
决定是生成新的fiber还是复用旧的fiber
function updateElement(returnFiber, current, element, lanes) {
if (current !== null) {
if (current.elementType === element.type || ( // Keep this check inline so it only runs on the false path:
isCompatibleFamilyForHotReloading(current, element) )) {
// Move based on index
var existing = useFiber(current, element.props);
existing.ref = coerceRef(returnFiber, current, element);
existing.return = returnFiber;
{
existing._debugSource = element._source;
existing._debugOwner = element._owner;
}
return existing;
}
} // Insert
var created = createFiberFromElement(element, returnFiber.mode, lanes);
created.ref = coerceRef(returnFiber, current, element);
created.return = returnFiber;
return created;
}
这时候current.elementType === element.type不相等,引用已经不同。
走了下面的新建Fiber createFiberFromElement, 返回的这个created结构如下,alternate是没有值,因为是新建的
deleteChild
从上面可知alternate为null,则reconcileChildrenArray中会走这个函数里
给fiber添加删除flags标记
function deleteChild(returnFiber, childToDelete) {
if (!shouldTrackSideEffects) {
// Noop.
return;
} // Deletions are added in reversed order so we add it to the front.
// At this point, the return fiber's effect list is empty except for
// deletions, so we can just append the deletion to the list. The remaining
// effects aren't added until the complete phase. Once we implement
// resuming, this may not be true.
var last = returnFiber.lastEffect;
if (last !== null) {
last.nextEffect = childToDelete;
returnFiber.lastEffect = childToDelete;
} else {
returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
}
childToDelete.nextEffect = null;
childToDelete.flags = Deletion;
}
placeChild
此时newFiber.flags = Placement,该节点被标记了替换
function placeChild(newFiber, lastPlacedIndex, newIndex) {
newFiber.index = newIndex;
if (!shouldTrackSideEffects) {
// Noop.
return lastPlacedIndex;
}
var current = newFiber.alternate;
if (current !== null) {
var oldIndex = current.index;
if (oldIndex < lastPlacedIndex) {
// This is a move.
newFiber.flags = Placement;
return lastPlacedIndex;
} else {
// This item can stay in place.
return oldIndex;
}
} else {
// This is an insertion.
newFiber.flags = Placement;
return lastPlacedIndex;
}
}
子元素递归
接下来进行下一轮递归
performUnitOfWork
var current = unitOfWork.alternate;这里有个赋值操作,
这里的unitOfWork就是 workInProgress
就是上一轮递归的next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
beginWork$1的返回值就是上一轮的updateElement的返回值新建的Fiber,是没有alternate引用的
function performUnitOfWork(unitOfWork) {
// The current, flushed, state of this fiber is the alternate. Ideally
// nothing should rely on this, but relying on it here means that we don't
// need an additional field on the work in progress.
var current = unitOfWork.alternate;
setCurrentFiber(unitOfWork);
var next;
if ( (unitOfWork.mode & ProfileMode) !== NoMode) {
startProfilerTimer(unitOfWork);
next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
} else {
next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
}
resetCurrentFiber();
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
// If this doesn't spawn new work, complete the current work.
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
ReactCurrentOwner$2.current = null;
}
beginWork
这里的workInProgress.tag是2,因为从上面可知,workInProgress 这是新建的Fiber对象,tag是默认2
function beginWork(current, workInProgress, renderLanes) {
switch (workInProgress.tag) {
case IndeterminateComponent:
{
return mountIndeterminateComponent(current, workInProgress, workInProgress.type, renderLanes);
}
}
}
mountIndeterminateComponent
function mountIndeterminateComponent(_current, workInProgress, Component, renderLanes) {
reconcileChildren(null, workInProgress, value, renderLanes);
return workInProgress.child;
}
reconcileChildren
function reconcileChildren(current, workInProgress, nextChildren, renderLanes) {
if (current === null) {
// If this is a fresh new component that hasn't been rendered yet, we
// won't update its child set by applying minimal side-effects. Instead,
// we will add them all to the child before it gets rendered. That means
// we can optimize this reconciliation pass by not tracking side-effects.
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes);
} else {
// If the current child is the same as the work in progress, it means that
// we haven't yet started any work on these children. Therefore, we use
// the clone algorithm to create a copy of all the current children.
// If we had any progressed work already, that is invalid at this point so
// let's throw it out.
workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderLanes);
}
}
mountChildFibers
var mountChildFibers = ChildReconciler(false);
ChildReconciler () {
reconcileChildFibers(){}
return reconcileChildFibers
}
mountChildFibers === reconcileChildFibers
function reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes) {
var isObject = typeof newChild === 'object' && newChild !== null;
if (isObject) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE:
return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes));
case REACT_PORTAL_TYPE:
return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, lanes));
}
}
}
reconcileSingleElement
currentFirstChild从上面传下来的是null
function reconcileSingleElement(returnFiber, currentFirstChild, element, lanes) {
var key = element.key;
var child = currentFirstChild;
while (child !== null) {
// TODO: If key === null and child.key === null, then this only applies to
// the first item in the list.
if (child.key === key) {
switch (child.tag) {}
}
if (element.type === REACT_FRAGMENT_TYPE) {
var created = createFiberFromFragment(element.props.children, returnFiber.mode, lanes, element.key);
created.return = returnFiber;
return created;
} else {
var _created4 = createFiberFromElement(element, returnFiber.mode, lanes);
_created4.ref = coerceRef(returnFiber, currentFirstChild, element);
_created4.return = returnFiber;
return _created4;
}
}
所以这里又新建了ul标签的Fiber节点