模拟实现
2023年06月11日
一、bailout
1.1 /packages/react-reconciler/beginWork.js beginWork()
export const beginWork = (workInProgress, renderLane) => {
// bailout 策略
didReceiveUpdate = false;
const current = workInProgress.alternate;
if (current != null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (oldProps !== newProps || current.type !== workInProgress.type) {
didReceiveUpdate = true;
} else {
const hasScheduledStateOrContext = checkScheduledUpdateOrContext(
current,
renderLane
);
if (!hasScheduledStateOrContext) {
didReceiveUpdate = false;
switch (workInProgress.tag) {
case ContextProvider:
const newValue = workInProgress.memoizedProps.value;
const context = workInProgress.type._context;
pushProvider(context, newValue);
break;
default:
break;
}
return bailoutOnAlreadyFinishedWork(workInProgress, renderLane);
}
}
}
workInProgress.lanes = NoLanes;
switch (workInProgress.tag) {
case HostRoot:
return updateHostRoot(workInProgress, renderLane);
case HostText:
return null;
case Fragment:
return updateFragment(workInProgress);
case HostComponent:
return updateHostComponent(workInProgress);
case FunctionComponent:
return updateFunctionComponent(workInProgress, renderLane);
case ContextProvider:
return updateContextProvider(workInProgress);
case SuspenseComponent:
return updateSuspenseComponent(workInProgress);
case OffscreenComponent:
return updateOffscreenComponent(workInProgress);
default:
console.log('beginWork 未实现的类型');
break;
}
return null;
};
1.2 /packages/react-reconciler/beginWork.js updateHostRoot()
function updateHostRoot(workInProgress, renderLane) {
const baseState = workInProgress.memoizedState;
const updateQueue = workInProgress.updateQueue;
const pending = updateQueue.shared.pending;
updateQueue.shared.pending = null;
const prevChildren = workInProgress.memoizedState;
const { memoizedState } = processUpdateQueue(baseState, pending, renderLane);
workInProgress.memoizedState = memoizedState;
const current = workInProgress.alternate;
if (current !== null) {
if (!current.memoizedState) {
current.memoizedState = memoizedState;
}
}
const nextChildren = workInProgress.memoizedState;
if (prevChildren === nextChildren) {
return bailoutOnAlreadyFinishedWork(workInProgress, renderLane);
}
reconcileChildren(workInProgress, nextChildren);
return workInProgress.child;
}
1.3 /packages/react-reconciler/beginWork.js updateFunctionComponent()
function updateFunctionComponent(workInProgress, renderLane) {
const nextChildren = renderWithHooks(workInProgress, renderLane);
const current = workInProgress.alternate;
if (current != null && !didReceiveUpdate) {
bailoutHook(workInProgress, renderLane);
return bailoutOnAlreadyFinishedWork(workInProgress, renderLane);
}
reconcileChildren(workInProgress, nextChildren);
return workInProgress.child;
}
1.4 /packages/react-reconciler/beginWork.js bailoutOnAlreadyFinishedWork()
function bailoutOnAlreadyFinishedWork(workInProgress, renderLane) {
if (!includeSomeLanes(workInProgress.childLanes, renderLane)) {
console.log('bailout 整颗子树');
return null;
}
console.log('bailout 当前 Fiber 子树');
cloneChildFibers(workInProgress);
return workInProgress.child;
}
二、eagerState
2.1 /packages/react-reconciler/fiberHooks.js
/**
* @description: dispatchSetState
* @param {*} fiber dispatchSetState.bind 时已经传入
* @param {*} updateQueue dispatchSetState.bind 时已经传入
* @param {*} action : setState(value) || setValue(()=> value) 中的 value 或者 ()=> value
*/
function dispatchSetState(fiber, updateQueue, action) {
const lane = requestUpdateLane();
const update = createUpdate(action, lane);
// eagerState 策略
const current = fiber.alternate;
if (
fiber.lanes === NoLanes &&
(current == null || current.lanes === NoLanes)
) {
const currentState = updateQueue.lastRenderedState;
const eagerState = basicStateReducer(currentState, action);
update.hasEagerState = true;
update.eagerState = eagerState;
if (Object.is(currentState, eagerState)) {
enqueueUpdate(updateQueue, update, fiber, NoLane);
console.log('命中 eagerState 策略', fiber);
return;
}
}
enqueueUpdate(updateQueue, update, fiber, lane);
scheduleUpdateOnFiber(fiber, lane);
}
function mountState(initialState) {
const hook = mountWorkInProgressHook();
let memoizedState;
if (initialState instanceof Function) {
memoizedState = initialState();
} else {
memoizedState = initialState;
}
const queue = createFCUpdateQueue();
hook.updateQueue = queue;
hook.baseState = memoizedState;
hook.memoizedState = memoizedState;
const dispatch = dispatchSetState.bind(null, currentlyRenderingFiber, queue);
queue.dispatch = dispatch;
queue.lastRenderedState = memoizedState;
return [memoizedState, dispatch];
}
function updateState() {
const hook = updateWorkInProgressHook();
const queue = hook.updateQueue;
const baseState = hook.baseState;
const pending = queue.shared.pending;
const current = currentHook;
let baseQueue = current.baseQueue;
if (pending != null) {
if (baseQueue != null) {
const baseFirst = baseQueue.next;
const pendingFirst = pending.next;
baseQueue.next = pendingFirst;
pending.next = baseFirst;
}
baseQueue = pending;
current.baseQueue = pending;
queue.shared.pending = null;
}
if (baseQueue !== null) {
const prevState = hook.memoizedState;
const {
memoizedState,
baseQueue: newBaseQueue,
baseState: newBaseState
} = processUpdateQueue(baseState, baseQueue, renderLane, update => {
const skippedLane = update.lane;
const fiber = currentlyRenderingFiber;
fiber.lanes = mergeLanes(fiber.lanes, skippedLane);
});
if (!Object.is(prevState, memoizedState)) {
markWorkInProgressReceiveUpdate();
}
hook.memoizedState = memoizedState;
hook.baseState = newBaseState;
hook.baseQueue = newBaseQueue;
queue.lastRenderedState = memoizedState;
}
return [hook.memoizedState, queue.dispatch];
}
2.2 /packages/react-reconciler/updateQueue.js
export function basicStateReducer(state, action) {
if (action instanceof Function) {
return action(state);
} else {
return action;
}
}
export const processUpdateQueue = (
baseState,
pendingUpdate,
renderLane,
onSkipUpdate
) => {
const result = { baseState, baseQueue: null, memoizedState: baseState };
if (pendingUpdate !== null) {
let first = pendingUpdate.next;
let pending = pendingUpdate.next;
let newBaseState = baseState;
let newBaseQueueFirst = null;
let newBaseQueueLast = null;
let newState = baseState;
do {
const updateLane = pending.lane;
if (!isSubsetOfLanes(renderLane, updateLane)) {
const clone = createUpdate(pending.action, pending.lane);
onSkipUpdate?.(clone);
if (newBaseQueueFirst === null) {
newBaseQueueFirst = clone;
newBaseQueueLast = clone;
newBaseState = newState;
} else {
newBaseQueueLast.next = clone;
newBaseQueueLast = clone;
}
} else {
if (newBaseQueueLast !== null) {
const clone = createUpdate(pending.action, NoLane);
newBaseQueueLast.next = clone;
newBaseQueueLast = clone;
}
const action = pending.action;
if (pending.hasEagerState) {
newState = pending.eagerState;
} else {
newState = basicStateReducer(baseState, action);
}
}
pending = pending.next;
} while (pending !== first);
if (newBaseQueueLast === null) {
newBaseState = newState;
} else {
newBaseQueueLast.next = newBaseQueueFirst;
}
result.memoizedState = newState;
result.baseState = newBaseState;
result.baseQueue = newBaseQueueLast;
}
return result;
};
三、测试
3.1 变化部分与不变部分分离
未分离之前: View
组件未订阅父组件中的任何状态, 理论上不应该随着 App
状态的改变而渲染。但是现在跟随者 num
状态的改变也发生了不必要的渲染。
function View(){
return <div> 嘻嘻哈哈 </div>
}
function App(){
const [num,setNum] = useState(0);
return <div>
<div>
<button onClick={ ()=> setNum(num+1) }> + 1</button>
<p> num is: { num }</p>
</div>
<View />
</div>
}
分离之后
: Num
组件中 num
状态的改变只影响 Num
组件自身, 对 App
组件和 View
组件毫无影响。
function View(){
return <div> 嘻嘻哈哈 </div>
}
function Num(){
const [num,setNum] = useState(0);
return <div>
<button onClick={ ()=> setNum(num+1) }> + 1</button>
<p> num is: { num }</p>
</div>
}
function App(){
return <div>
<Num />
<View />
</div>
}