跳到主要内容

模拟实现

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