2021-05-18
React 机制基础
requirements
使用过 React
前言
请注意本文 Work In Progress
Fiber Tag
ReactWorkTag
列出了 fiber 的 tag 类型。
TypeScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
export const FunctionComponent = 0; export const ClassComponent = 1; export const IndeterminateComponent = 2; // Before we know whether it is function or class export const HostRoot = 3; // Root of a host tree. Could be nested inside another node. export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer. export const HostComponent = 5; export const HostText = 6; export const Fragment = 7; export const Mode = 8; export const ContextConsumer = 9; export const ContextProvider = 10; export const ForwardRef = 11; export const Profiler = 12; export const SuspenseComponent = 13; export const MemoComponent = 14; export const SimpleMemoComponent = 15; export const LazyComponent = 16; export const IncompleteClassComponent = 17; export const DehydratedFragment = 18; export const SuspenseListComponent = 19; export const FundamentalComponent = 20; export const ScopeComponent = 21; export const Block = 22; export const OffscreenComponent = 23; export const LegacyHiddenComponent = 24;
若有如下代码:
JSX
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
const context = React.createContext(); class ClassComp extends React.Component { render = () => this.props.children || null; } class PureComp extends React.PureComponent { render = () => this.props.children || null; } const FunctionComp = ({ children = null }) => children; const Memozied = React.memo(FunctionComp); const NotSimpleMemozied = React.memo(FunctionComp, () => true); const style = { display: "none" }; const spanStyle = { whiteSpace: "pre", fontFamily: "Consolas", }; export default function App() { const ref = React.useRef(null); useEffect(() => { getReadableFiberTree("root", (text) => { ref.current.innerText = text; }); }, []); return ( <div> <div style={style}> <FunctionComp> <> <span>span-text</span> <Memozied></Memozied> <NotSimpleMemozied></NotSimpleMemozied> text </> </FunctionComp> <context.Provider value={"provider-text"}> <ClassComp> <context.Consumer>{(val) => <span>{val}</span>}</context.Consumer> </ClassComp> </context.Provider> <React.Fragment> <PureComp /> </React.Fragment> </div> <div style={spanStyle} ref={ref}></div> </div> ); }
其 Fiber 树如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
HostRoot: Mode: FunctionComponent: HostComponent: HostComponent: FunctionComponent: HostComponent SimpleMemoComponent(react.memo) MemoComponent(react.memo): FunctionComponent HostText ContextProvider(react.provider): ClassComponent: ContextConsumer(react.context): HostComponent Fragment: ClassComponent HostComponent
Lane 模型
lane 意思是 “车道”,是 React 中的优先级模型。
Lane
为单独的优先级,Lanes
则是表示有多个优先级。
TypeScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
export const NoLanes: Lanes = /* */ 0b0000000000000000000000000000000; export const NoLane: Lane = /* */ 0b0000000000000000000000000000000; export const SyncLane: Lane = /* */ 0b0000000000000000000000000000001; export const SyncBatchedLane: Lane = /* */ 0b0000000000000000000000000000010; export const InputDiscreteHydrationLane: Lane = /* */ 0b0000000000000000000000000000100; const InputDiscreteLanes: Lanes = /* */ 0b0000000000000000000000000011000; const InputContinuousHydrationLane: Lane = /* */ 0b0000000000000000000000000100000; const InputContinuousLanes: Lanes = /* */ 0b0000000000000000000000011000000; export const DefaultHydrationLane: Lane = /* */ 0b0000000000000000000000100000000; export const DefaultLanes: Lanes = /* */ 0b0000000000000000000111000000000; const TransitionHydrationLane: Lane = /* */ 0b0000000000000000001000000000000; const TransitionLanes: Lanes = /* */ 0b0000000001111111110000000000000; const RetryLanes: Lanes = /* */ 0b0000011110000000000000000000000; export const SomeRetryLane: Lanes = /* */ 0b0000010000000000000000000000000; export const SelectiveHydrationLane: Lane = /* */ 0b0000100000000000000000000000000; const NonIdleLanes = /* */ 0b0000111111111111111111111111111; export const IdleHydrationLane: Lane = /* */ 0b0001000000000000000000000000000; const IdleLanes: Lanes = /* */ 0b0110000000000000000000000000000; export const OffscreenLane: Lane = /* */ 0b1000000000000000000000000000000;
LanePriority
越大则优先级越高。
TypeScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
export const SyncLanePriority: LanePriority = 15; export const SyncBatchedLanePriority: LanePriority = 14; const InputDiscreteHydrationLanePriority: LanePriority = 13; export const InputDiscreteLanePriority: LanePriority = 12; const InputContinuousHydrationLanePriority: LanePriority = 11; export const InputContinuousLanePriority: LanePriority = 10; const DefaultHydrationLanePriority: LanePriority = 9; export const DefaultLanePriority: LanePriority = 8; const TransitionHydrationPriority: LanePriority = 7; export const TransitionPriority: LanePriority = 6; const RetryLanePriority: LanePriority = 5; const SelectiveHydrationLanePriority: LanePriority = 4; const IdleHydrationLanePriority: LanePriority = 3; const IdleLanePriority: LanePriority = 2; const OffscreenLanePriority: LanePriority = 1; export const NoLanePriority: LanePriority = 0;
Lanes 的运算
TypeScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
// 取 lanes 中最高的 lane/lanes function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes; /* 取 lanes 中取 lane 操作 */ function getHighestPriorityLane(lanes: Lanes) { return lanes & -lanes; } function getLowestPriorityLane(lanes: Lanes): Lane { // This finds the most significant non-zero bit. const index = 31 - clz32(lanes); return index < 0 ? NoLanes : 1 << index; } export function pickArbitraryLane(lanes: Lanes): Lane; function pickArbitraryLaneIndex(lanes: Lanes); // ... /* lanes 集合操作 */ export function includesSomeLane(a: Lanes | Lane, b: Lanes | Lane) { return (a & b) !== NoLanes; } export function isSubsetOfLanes(set: Lanes, subset: Lanes | Lane) { return (set & subset) === subset; } export function mergeLanes(a: Lanes | Lane, b: Lanes | Lane): Lanes { return a | b; } export function removeLanes(set: Lanes, subset: Lanes | Lane): Lanes { return set & ~subset; } export function findUpdateLane( lanePriority: LanePriority, wipLanes: Lanes ): Lane;
其中,
getHighestPriorityLanes
:同时维护变量return_highestLanePriority
;findUpdateLane(lanePriority: LanePriority, wipLanes: Lanes)
:会取优先级最高为 lanePriority 的,尽量排除 wipLanes 的 lane;
与 Scheduler 中的优先级
lane 与 Scheduler 中的优先级不同,因此 schedulerPriorityToLanePriority
, lanePriorityToSchedulerPriority
可实现优先级互转。
FiberRoot 相关
TypeScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14
type FiberRoot = { eventTimes: LaneMap<number>; expirationTimes: LaneMap<number>; pendingLanes: Lanes; suspendedLanes: Lanes; pingedLanes: Lanes; expiredLanes: Lanes; mutableReadLanes: Lanes; finishedLanes: Lanes; entangledLanes: Lanes; entanglements: LaneMap<Lanes>; };
markRootFinished(root, remainingLanes)
:用remainingLanes
视为新的pendingLanes
并更新 root 上的各个 lanes 属性与LaneMap
等;markStarvedLanesAsExpired(root, currentTime)
:根据 currentTime 将root.pendingLanes
中的 lane 合并到root.expiredLanes
中, 此 API 用于防 lane 饥饿;markRootUpdated
/markRootSuspended
/markRootPinged
/markRootExpired
/...;getNextLanes(root: FiberRoot, wipLanes: Lanes)
:根据 root 上的 lanes 信息与 wipLanes 得到应被优先处理的 lanes。在一次 render 过程中,若 nextLanes 非 NoLane 且 wipLanes 优先级更高则会返回 wipLanes,以防止任务中断;
维护变量
currentUpdateLanePriority