react v16.13.1 源码阅读
Jarweb opened this issue · comments
reactElement
$$typeof: Symbol(react.element)
key: null
props:
children:
$$typeof: Symbol(react.element)
key: null
props: {}
ref: null
type: ƒ App()
_owner: null
_store: {validated: true}
_self: null
_source: {fileName: "/Users/renjiazheng/Jar/jar-github/read-source/read-source/src/index.js", lineNumber: 9, columnNumber: 5}
__proto__: Object
__proto__: Object
ref: null
type: Symbol(react.strict_mode)
_owner: null
_store: {validated: false}
const Demo = (props) => {
return (<div className="demo">demo, {props.data}</div>)
}
const App = () => {
return (
<div className="app">
<Demo data={'hi'} />
</div>
)
}
// ==> jsx to react element
var Demo = function Demo(props) {
// type: html tag/comp/内建组件
// props: props/config/ref/key 等
// children: 后面的参数都是 children
return React.createElement("div", {className: 'demo'}, "demo, ", props.data);
};
var App = function App() {
return React.createElement("div", {className: 'app'}, React.createElement(Demo, {
data: 'hi'
}));
// 所有的 jsx 都会被转成 createElement,最终导出的就只有一个 App 函数,进入 render
- 函数组件/类组件的 render 返回值就是 react element
- type
- String,表示原生 dom,称为 hostcomponent
- class,classscomponent 本身
- function,functioncomponent 本身
- symbol,如:
- symbol(react.strict_mode)
- symbol(react.portal)
- symbol(react.profile)
- symbol(react.fragment)
- symbol(react.provider)
- symbol(react.context)
- symbol(react.forward_ref)
- symbol(react.suspense)
- symbol(react.memo)
- symbol(react.lazy)
- ...
- $$typeof
- ReactElememt 函数创建的 element 都是 symbol(react.element)
- props
- 接收的 props 和 children
FiberRoot
function FiberRootNode(containerInfo, tag, hydrate) {
this.tag = tag;
this.current = null; // fiber tree 的根节点
this.containerInfo = containerInfo; // root dom
this.hydrate = hydrate; // 是否要与以后的 dom 融合
this.pendingChildren = null;
this.pingCache = null;
this.finishedExpirationTime = NoWork;
this.finishedWork = null;
this.timeoutHandle = noTimeout;
this.context = null;
this.pendingContext = null;
this.callbackNode = null;
this.callbackPriority = NoPriority;
this.firstPendingTime = NoWork;
this.firstSuspendedTime = NoWork;
this.lastSuspendedTime = NoWork;
this.nextKnownPendingLevel = NoWork;
this.lastPingedTime = NoWork;
this.lastExpiredTime = NoWork;
this.interactionThreadID = tracing.unstable_getThreadID();
this.memoizedInteractions = new Set();
this.pendingInteractionMap = new Map();
}
FiberNode
function FiberNode(tag, pendingProps, key, mode) {
this.tag = tag; // 不同的组件类型
this.key = key; // react element 的 key
this.elementType = null; // react element 的 type
this.type = null; // 异步组件resolved之后返回的内容,一般是`function`或者`class`
this.stateNode = null; // 跟当前Fiber相关本地状态(比如浏览器环境就是DOM节点)
this.return = null; // 指向他在Fiber节点树中的`parent`,用来在处理完这个节点之后向上返回
this.child = null; // 指向自己的第一个子节点
this.sibling = null; // 兄弟节点的return指向同一个父节点
this.index = 0;
this.ref = null; // ref
this.pendingProps = pendingProps; // 新的 props
this.memoizedProps = null; // 旧的 props
this.memoizedState = null; // 新旧 state
this.updateQueue = null; // 该Fiber对应的组件产生的Update会存放在这个队列里面
this.dependencies = null;
this.mode = mode; // Effects 表示这个子树是否默认是异步渲染的
this.effectTag = NoEffect; // 用来记录Side Effect
this.nextEffect = null; // 用来快速查找下一个side effect
this.firstEffect = null; // 第一个side effect
this.lastEffect = null; // 最后一个side effect
this.expirationTime = NoWork; // 过期时间
this.childExpirationTime = NoWork; // 快速确定子树中是否有不在等待的变化
this.alternate = null; // `current <==> workInProgress` 在渲染完成之后他们会交换位置,类似于备份,方便重用
}
tag
- fiber node 类型
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; // html tag
export const HostText = 6; // text
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;
effectTags
- 副作用类型,标记每个更新的类型,作相应的处理
export const NoEffect = /* */ 0b00000000000;
export const PerformedWork = /* */ 0b00000000001;
// You can change the rest (and add more).
export const Placement = /* */ 0b00000000010; // 插入/移到
export const Update = /* */ 0b00000000100; // props 更新
export const PlacementAndUpdate = /* */ 0b00000000110;
export const Deletion = /* */ 0b00000001000;
export const ContentReset = /* */ 0b00000010000;
export const Callback = /* */ 0b00000100000;
export const DidCapture = /* */ 0b00001000000;
export const Ref = /* */ 0b00010000000;
export const Snapshot = /* */ 0b00100000000;
// Update & Callback & Ref & Snapshot
export const LifecycleEffectMask = /* */ 0b00110100100;
// Union of all host effects
export const HostEffectMask = /* */ 0b00111111111;
export const Incomplete = /* */ 0b01000000000;
export const ShouldCapture = /* */ 0b10000000000;
update & updateQueue
- 调用更新函数会生成 update 对象,update 对象会维护在对应组件的 fiber node 上
// 通过链表来存储所有的 update
export type Update<State> = {
// 更新的过期时间
expirationTime: ExpirationTime,
// export const UpdateState = 0;
// export const ReplaceState = 1;
// export const ForceUpdate = 2;
// export const CaptureUpdate = 3;
// 指定更新的类型,值为以上几种
tag: 0 | 1 | 2 | 3,
// 更新内容,比如`setState`接收的第一个参数,函数或者一个 state 对象
payload: any,
// 对应的回调,`setState`,`render`都有
callback: (() => mixed) | null,
// 指向下一个更新
next: Update<State> | null,
// 指向下一个`side effect`
nextEffect: Update<State> | null,
};
// 一个普通的对象
export type UpdateQueue<State> = {
// 每次操作完更新之后的`state`,当更新阶段,basestate 存的是 state
baseState: State,
// 队列中的第一个`Update`
firstUpdate: Update<State> | null,
// 队列中的最后一个`Update`
lastUpdate: Update<State> | null,
// 第一个捕获类型的`Update`
firstCapturedUpdate: Update<State> | null,
// 最后一个捕获类型的`Update`
lastCapturedUpdate: Update<State> | null,
// 第一个`side effect`
firstEffect: Update<State> | null,
// 最后一个`side effect`
lastEffect: Update<State> | null,
// 第一个和最后一个捕获产生的`side effect`
firstCapturedEffect: Update<State> | null,
lastCapturedEffect: Update<State> | null,
};
渲染阶段
-
render(element, container, callback)
-
return legacyRenderSubtreeIntoContainer(null, element, container, false, callback)
-
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate)
- 创建 fiber root,如果不是 hydrate 模式,移除 dom root 内的节点
- new FiberRootNode,创建 fiber root 实例
- 同时参加 hostFiber,createFiber(HostRoot, null, null, mode),即 fiber tree 的根节点
- 将 hostFiber 赋给 root.current,hostFiber.stateNode = root,stateNode 对应着 fiber root
-
封装 render 传入的 callback
-
updateContainer(children, fiberRoot, parentComponent, callback)
-
scheduleWork(current$1, expirationTime) ,即执行 scheduleUpdateOnFiber 函数
-
performSyncWorkOnRoot(root),同步的任务处理,还有 performConcurrentWorkOnRoot 模式的
-
workLoopSync(),执行该函数直到完成,执行不同组件的初始化,生成 fiber node
- workInProgress = performUnitOfWork(workInProgress),循环执行该函数,直到 workInProgress === null 退出循环
- next = beginWork(current, unitOfWork, renderExpirationTime$1)
- 根据 fiber.tag 的类型来处理不同情况:
- IndeterminateComponent,不确定什么类型,mountIndeterminateComponent
- 当作 class component 或者 func component 处理
- LazyComponent,mountLazyComponent
- refineResolvedLazyComponent,检查组件是否 resolve
- 当组件没有 resolve,会被抛出 throw
- 当组件 resolve 之后,按照组件的 tag,进行相应的处理
- FunctionComponent,updateFunctionComponent
- renderWithHooks(current, workInProgress, Component, nextProps, context, renderExpirationTime)
- 注入 ReactCurrentDispatcher,给 hook 提供通信
- var children = Component(props, secondArg),执行函数组件得到 react element
函数组件执行
,这里可能多次执行- reconcileChildren(current, workInProgress, nextChildren, renderExpirationTime),调和子组件
- ClassComponent,updateClassComponent
- constructClassInstance(workInProgress, Component, nextProps)
- var instance = new ctor(props, context),
组件实例化
- mountClassInstance(workInProgress, Component, nextProps, renderExpirationTime)
- applyDerivedStateFromProps,执行
getDerivedStateFromProps
- callComponentWillMount,执行
componentWillMount
- finishClassComponent(current, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime)
- reconcileChildren(current, workInProgress, nextChildren, renderExpirationTime),调和子组件, 即 vdom diff 算法的过程,最后在 fiber tree 上,生成子的 fiber node
- 根据 element 的 $$ typeof,string/number,array,interator 进行相应的处理
- reconcileSingleElement
- 如果是首次渲染,直接创建 fiber node,createFiberFromFragment(element.props.children, returnFiber.mode, expirationTime, element.key)
- 如果是更新阶段
- 如果 $$typeof 是 react.element
- fragment:返回 children 的 fiber node,并删除兄弟节点
- reconcileSingleTextNode
- createFiberFromText
- reconcileChildrenArray
- reconcileChildrenIterator
- HostRoot,updateHostRoot
- HostComponent,dom 类型,如 div 等,updateHostComponent
- HostText,updateHostText
- SuspenseComponent,updateSuspenseComponent
- HostPortal,updatePortalComponent
- ForwardRef,updateForwardRef
- Fragment,updateFragment
- Mode,updateMode
- Profiler,updateProfiler
- ContextProvider,updateContextProvider
- ContextConsumer,updateContextConsumer
- MemoComponent,updateMemoComponent
- SimpleMemoComponent,updateSimpleMemoComponent
- IncompleteClassComponent,mountIncompleteClassComponent
- SuspenseListComponent,updateSuspenseListComponent
- next = completeUnitOfWork(unitOfWork),当 next === null 即叶子结点时,执行 completeUnitOfWork
- next = completeWork(current, workInProgress, renderExpirationTime$1)
- HostComponent,updateHostComponent
- prepareUpdate
- diffProperties(domElement, type, oldProps, newProps, rootContainerInstance)
- 样式属性,dom 属性
事件绑定
,ensureListeningTo(rootContainerElement, propKey)- legacyListenToEvent(registrationName, doc),事件都是绑定到 doc 上的。相关事件也会绑定,比如只绑定了 input onchange,其他*["blur", "change", "click", "focus", "input", "keydown", "keyup", "selectionchange"] 也一起进行绑定*
- legacyListenToTopLevelEvent
- trapCapturedEvent/trapBubbledEvent
- trapEventForPluginEventSystem
- 分为4种事件监听回调
- DiscreteEvent,dispatchDiscreteEvent
- UserBlockingEvent,dispatchUserBlockingUpdate
- ContinuousEvent,dispatchEvent
- 其他,dispatchEvent
- addEventCaptureListener(container, rawEventName, listener)
- addEventBubbleListener(container, rawEventName, listener)
- createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress)
- var domElement = createElement(type, props, rootContainerInstance, parentNamespace)
- precacheFiberNode(internalInstanceHandle, domElement)
- updateFiberProps(domElement, props),事件回调处理
- node[internalEventHandlersKey] = props,事件回调对象会挂到 dom node 上,如:input {onFocus: ƒ, onChange: ƒ}
- appendAllChildren(instance, workInProgress, false, false)
- HostText,updateHostText
- next = completeWork(current, workInProgress, renderExpirationTime$1)
- next = beginWork(current, unitOfWork, renderExpirationTime$1)
- workInProgress = performUnitOfWork(workInProgress),循环执行该函数,直到 workInProgress === null 退出循环
-
finishSyncRender,对 fiber node 生成 dom 片段,插入 dom
-
commitRoot,commitRootImpl,
-
commitBeforeMutationEffects
- commitBeforeMutationLifeCycles
- flushPassiveEffects
- commitPassiveHookEffects
- commitHookEffectListUnmount,执行 hook 的 clean。hook 存在 fiber node 的 lasteffect/firsteffect 上
- commitHookEffectListMount,执行 hook 的 create
- flushSyncCallbackQueue
- commitPassiveHookEffects
-
commitMutationEffects
- commitResetTextContent,清空 innertext
- commitDetachRef,设置 ref current 为 null
- commitPlacement,插入到父节点
- insertOrAppendPlacementNodeIntoContainer
- insertOrAppendPlacementNode
- commitWork
- FunctionComponent,ForwardRef,SimpleMemoComponent
- commitHookEffectListUnmount,限制性 effect cleanup
- commitUpdate
- updateFiberProps,更新事件回调
- updateProperties
- updateDOMProperties,更新 dom 属性,不同的表单类型处理不一样
- commitTextUpdate
- commitSuspenseComponent
- hideOrUnhideAllChildren,通过 css display none 来显示隐藏节点
- FunctionComponent,ForwardRef,SimpleMemoComponent
- commitDeletion
- unmountHostComponents
- commitNestedUnmounts
- commitUnmount
- removeChildFromContainer
- removeChild
- commitUnmount
- onCommitUnmount
- safelyCallDestroy,hook destory 清除副作用
- safelyDetachRef,重置 ref
- safelyCallComponentWillUnmount,执行 unmount 函数
- detachFiber,重置 fiber node 的 alternate 属性
- unmountHostComponents
-
commitLayoutEffects,执行生命周期函数
commitLifeCycles
- func comp,commitHookEffectListMount
- 执行 useEffect 的 create 函数,同时得到 destory
- effect.destory = create()
- class comp
- instance.componentDidMount(),执行 didmount
- instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate), 执行 didupdate
- commitUpdateQueue(finishedWork, updateQueue, instance)
- hostcomponent
- commitMount(_instance2, type, props),dom focus
- HostRoot
- commitUpdateQueue
- SuspenseComponent
- commitSuspenseHydrationCallbacks
- func comp,commitHookEffectListMount
- commitAttachRef
- 给 fiber.ref 赋值 fiber.stateNode, stateNode 就是 dom 实例
-
flushSyncCallbackQueue,执行 render 传入的 callback
-
-
-
-
-
-
-
总结
- beginWork 阶段
- 组件实例化,执行部分生命周期函数
- reconcileChildren 调和子组件,生成子组件的 fiber node,子组件实例化,执行自身生命周期函数
- completeUnitOfWork 对 html tag/text 节点进行 dom 创建,属性挂载/事件绑定
- finishSyncRender 阶段
- dom 片段生成,执行剩余生命周期函数
- beginWork 阶段
更新阶段
-
setstate
-
var fiber = get(inst),根据 instance 找到对应的 fiber node
-
enqueueSetState,创建一个 update,将 update 存到链表中的 sharedQueue.pending
-
scheduleWork,即 scheduleUpdateOnFiber,进入循环更新
-
// 为更新创建对应的 update 对象 callback: null expirationTime: 1073741823 // 过期时间 next: // 通过链表链接其他的 update callback: null expirationTime: 1073741823 next: {expirationTime: 1073741823, suspenseConfig: null, tag: 0, payload: {…}, callback: null, …} payload: {count: 3} // setstate({count: 3}) priority: 98 suspenseConfig: null tag: 0 __proto__: Object payload: {count: 3} // setstate 的 state priority: 98 // 优先级 suspenseConfig: null tag: 0 // fiber 上的 updatequeue 对象 updateQueue: baseState: {count: 1} // 最初的 state,更新后的 state 会更新这里 effects: null baseQueue: { // 已处理的 update 链 callback: null expirationTime: 1073741823 next: {…} payload: {count: 2} priority: 97 suspenseConfig: null tag: 0 } shared: { pending: { // shared.pending,等待处理的 update callback: null expirationTime: 1073741823 next: { callback: null expirationTime: 1073741823 next: {expirationTime: 1073741823, suspenseConfig: null, tag: 0, payload: {…}, callback: null, …} payload: {count: 3} // 第一个 setstate priority: 98 suspenseConfig: null tag: 0 } payload: {count: 4} // 并列的第二个 setstate,会批量更新 priority: 98 suspenseConfig: null tag: 0 } }
-
只有一个节点时,update 对象的 next 执行自身。循环链表
-
payload,setstate 传入的对象
-
当连续 setstate 时
-
正常情况:
- 每个 setstate 会创建一个 update 存到 shared.pending 对象上,然后通过循环链表 next 来连起所有的 update,等到事件回调执行完才去进行调度更新,达到批量更新的效果
-
事件循环情况:
-
当 setState 被推到事件循环中时,由于事件循环的机制,所有的任务都是分开执行的,所以每个 setstate 会执行一次调度更新
-
const handle = () => { // 两个 setTimeout 的任务是在事件循坏中分开执行的,当事件 handle 执行完后,不会有调度更新,当一个 settimeout task 执行时,会进行一次更新 setTimeout(() => { // 里面的 setstate 也是逐个更新的,不是批量的,为什么?因为 excutionContext 是 nocontext。默认情况下就是每个更新动作都会触发一次调度更新。react 针对合成事件/生命函数进行了优化,阻止了调度更新。把调度更新移到了合成事件/生命函数执行完之后。使得一次合成事件执行,里面即使有多个更新动作,也只在最后才触发一次调度更新。 this.setState({ count: 0 }) this.setState({ count: 1 }) }, 0) setTimeout(() => { this.setState({ count: 0 }) }, 0) }
-
-
-
-
-
forceupdate
- var fiber = get(inst),根据 instance 找到对应的 fiber
- enqueueForceUpdate,创建 update,挂在 updateQueue 在当前的 fiber
- update.tag = ForceUpdate
- scheduleWork
-
replaceState
- 跟 setState、forceUpdate 一样的逻辑
- update.tag = ReplaceState
- scheduleWork
-
useState
- dispatchAction
- scheduleWork
- dispatchAction
-
总结
-
触发更新动作,创建 update 存到对应的 fiber.updateQueue.shared.pending.payload 上,当多个更新触发时,会通过 next 指针形成链表。
-
每个 update 都是由更新动作触发而创建的。forceupdate/replacestate/setstate/usestate 的调用都会转化为一个个 update 对象,最后形成 update 链表
-
多个 setState 触发会如何?
- 多个 setstate 在一个事件回调里面,需要等到事件回调执行完成。setstate 触发 scheduleWork 但不会进行 performSyncWorkOnRoot。而是等到事件回调执行完成。
- 执行 ensureRootIsScheduled 来将一个个 performSyncWorkOnRoot 存进一个数组里。
- 等事件回调执行完,会通过 flushSyncCallbackQueueImpl 方法将数组里的 performSyncWorkOnRoot 取出执行。这样就进入更新调度了
-
事件系统
-
事件是如何绑定的?
-
p 标签上有 onclick 事件,p 标签的父是 div
-
在 div 对应的 fiber 调和子节点的时候生成 p 的 fiber,p 的事件还存在 p fiber.pendingProps 上。beginwork 完成后会将 fiber.pendingProps 上的属性存到 fiber.memoizedProps 上。即事件回调存到了 fiber.memoizedProps 上
-
在 completeWork 时,创建 fiber.stateNode。然后在 updateFiberProps 函数中 fiber.memoizedProps 赋给 fiber.stateNode
-
// 事件回调存在 fiber.stateNode 上,不管是函数组件还是类组件 fiber.stateNode: { handleClick: f, handleChange: f, }
-
-
渲染阶段,在 finalizeInitialChildren 函数里进行
-
setInitialProperties,对某些标签的事件进行绑定,如:load,error,reset,submit,toggle,invalid 等,对表单元素会做很多额外的封装
-
setInitialDOMProperties,设置 dom style,innerhtml,innertext,通过 ensureListeningTo 进行事件绑定等。这里的事件绑定是代理到 dom root/document 的
-
ensureListeningTo,除了绑定对应的事件,还有其他相同组的事件,
-
如:绑定的是 onchange,但 react 同时还会绑定 ["blur", "change", "click", "focus", "input", "keydown", "keyup", "selectionchange"]
-
react 会将事件回调进行封装。比如,onchange = () => {console.log('hi')}。这个事件回调是存在 fiber.stateNode 上的
-
在事件绑定的时候,通过 dispatchDiscreteEvent 返回事件回调。即事件回调是被 dispatchDiscreteEvent 封装了
-
// null,click,1,body listener = dispatchDiscreteEvent.bind(null, topLevelType, PLUGIN_EVENT_SYSTEM, container) discreteUpdates(dispatchEvent, topLevelType, eventSystemFlags, container, nativeEvent) dispatchEvent var blockedOn = attemptToDispatchEvent(topLevelType, eventSystemFlags, container, nativeEvent); // 回去对应的 dom 实例,即 fiber.stateNode queueDiscreteEvent(blockedOn, topLevelType, eventSystemFlags, container, nativeEvent)
-
discreteUpdates
-
-
-
-
事件是如何执行的?
- 事件会代理在 dom root 上。当事件触发时,dom root 会监听到事件触发,然后执行事件回调
- 每个事件回调都会被 dispatchEvent 封装,通过 runWithPriority 进行调度执行
hook
- hook 是如何存储的?
const App = () => {
const [a, seta] = useState(11) // hook 1
const ref = useRef('ref') // hook 2
const [b, setb] = useState(22) // hook 3
useEffect(() => { // hook 4
console.log('effect')
return () => {
console.log('clean')
}
}, [])
return (<div>app</div>)
}
// 当前 fiber node
...
elementType: ƒ App()
memoizedState: {
baseQueue: null
baseState: 11
memoizedState: 11 // hook 1
next: {
baseQueue: null
baseState: null
memoizedState: {current: div.App} // hook 2
next: {
baseQueue: null
baseState: 22
memoizedState: 22 // hook 3
next: { // hook 4, effect hook 没有 memoizedState
create: () => {…} // effect
deps: null
destroy: () => { console.log('clean'); } // effect cleanup
next: {tag: 5, deps: null, next: {…}, create: ƒ, destroy: ƒ}
tag: 5
}
queue: {
dispatch: ƒ () // setb
lastRenderedReducer: ƒ basicStateReducer(state, action)
lastRenderedState: 22
pending: null
}
}
queue: null
}
}
queue: {
dispatch: ƒ () // seta
lastRenderedReducer: ƒ basicStateReducer(state, action)
lastRenderedState: 11
pending:
}
}
-
hook 是如何执行的?
-
useState
-
mountState(initialState)
-
创建一个空的 hook 对象,计算 initialstate,因为有可能是函数,将 state 存到 hook.baseState 上,同时初始化 hook.queue, 绑定 hook.dispatcher
-
hook: { baseQueue: null baseState: null memoizedState: null next: null queue: null } hook.memoizedState = hook.baseState = initialState hook.queue = { pending: null, dispatch: null, lastRenderedReducer: basicStateReducer, lastRenderedState: initialState } dispatch = hook.queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber$1, queue) // [a, seta] = useState(1) return [hook.memoizedState, dispatch]
-
-
dispatchAction
- scheduleWork
seta(33) dispatchAction(fiber, queue, action) // 参数: action: 33 queue: { dispatch: ƒ () lastRenderedReducer: ƒ basicStateReducer(state, action) lastRenderedState: 11 pending: { action: 33 eagerReducer: ƒ basicStateReducer(state, action) eagerState: 33 expirationTime: 1073741823 next: {expirationTime: 1073741823, suspenseConfig: null, action: 33, eagerState: 33, eagerReducer: ƒ, …} priority: 98 } } 然后创建一个 update 接着开始调度更新:scheduleWork(fiber, expirationTime) // 所有更新最终都是结果 scheduleWork 来调度
-
-
useRef
-
mountRef(initialValue)
-
创建一个空的 hook 对象,创建 ref 对象,将 ref 对象存到 hook 上
-
hook:{ baseQueue: null baseState: null memoizedState: null next: null queue: null } ref: { current: initialvalue } hook.memoizedState = ref
-
-
-
useEffect
-
mountEffect(create, deps)
-
mountEffectImpl(Update | Passive, Passive$1, create, deps)
-
创建一个 hook,标记 effectTag = fiberEffectTag,将 effect 挂到 hook.memoizedState 上
-
hook: { baseQueue: null baseState: null memoizedState: {tag: 5, destroy: undefined, deps: null, next: {…}, create: ƒ} next: null queue: null }
-
-
-
useCallback
- mountCallback(callback, deps)
- hook.memoizedState = [callback, nextDeps]
- mountCallback(callback, deps)
-
useContext
-
readContext(callback, deps)
-
// 首个 context lastContextDependency = contextItem; currentlyRenderingFiber.dependencies = { // 在 fiber dependencies 上存储 context 信息 expirationTime: NoWork, firstContext: { context: context, observedBits: deps, next: null }, responders: null }; // 新增的 context lastContextDependency = lastContextDependency.next = { context: context, observedBits: deps, next: null }
-
-
useLayoutEffect
-
mountLayoutEffect(create, deps)
-
mountEffectImpl
-
mountEffectImpl(Update | Passive, Passive$1, create, deps) // useEffect mountEffectImpl(Update, Layout, create, deps) // useLayoutEffect
-
-
-
useReducer
- mountReducer,和 usestate 的逻辑差不多
-
当组件多长执行时,只是重复创建 hook ,相当于 hook 的初始化,不会进行 hook 的执行。hook 还没有和 fiber 相关联
-
但 seta 的时候,会进行 dispatch 当前的 fiber 和当前的 hook.queue
-
context
- context 存在哪里?也是 fiber.stateNode 上
- todo
ref
- todo
react element
$$typeof: Symbol(react.element)
key: null
props: {className: "App", children: Array(3)}
ref: {current: "hiref"}
type: "div"
_owner: FiberNode {tag: 2, key: null, stateNode: null, elementType: ƒ, type: ƒ, …}
_store: {validated: false}
concurrent mode
- todo
整体的 fiber tree
class DemoCls extends React.Component {
state = {
count: 1
}
handle = () => {
this.setState({
count: 3
})
}
render() {
return (
<div onClick={this.handle}>class, {this.state.count}</div>
)
}
}
function App() {
const [a, seta] = useState(11)
const ref = useRef('hiref')
const [b, setb] = useState(22)
useEffect(() => {
console.log('effect')
return () => {
console.log('clean')
}
})
const handle = () => {
console.log(ref)
}
const handleChange = () => {
console.log('input')
}
const handleFocus = () => {
console.log('focus')
}
return (
<div>
<p
onClick={handle}
style={{color: '#fff'}}
className="test"
id="test"
key="test"
ref={ref}
>
say <span>hi</span> haaa!
</p>
<input
id="inpuid"
onFocus={handleFocus}
onChange={handleChange}
/>
<DemoCls />
</div>
);
}
render(<App />, root)
fiberRoot {
containerInfo: 指向 dom root
current: 指向 root fiber,fiber tree 的根节点
}
hostFiber { // fiber tree 的根节点
alternate: 自身
stateNode: 指向 fiberRoot
type: null
child: 指向 App 的 fiber
}
App fiber {
alternate: 指向自身
stateNode: null
type: f App() // 组件
elemenType: f App() // 组件
return: 指向 host fiber
sibling:null
child:指向 div fiber
firstEffect: 需要更新的 fiber
lastEffect: 需要更新的 fiber
nextEffect: null
memoizedProps: {}
memoizedState: {
baseQueue: null
baseState: 11
memoizedState: 11
queue: {
dispatch: f // seta
lastRenderedReducer: f
lastRenderedState: 11
pending: null
}
next: {
baseQueue: null
baseState: null
memoizedState: {
current: f
}
queue: null
next: {
baseQueue: null
baseState: 22
memoizedState: 22
queue: {
dispatch: f // setb
lastRenderedReducer: f
lastRenderedState: 22
pending: null
}
next: {
baseQueue: null
baseState: null
memoizedState: {
create: f
deps: null
destroy: f
next: {}
}
queue: null
}
}
}
}
pendingProps: {}
ref: null
updateQueue: {
lastEffect: { // useEffect
create: () => {...}
deps: null
destroy: () => {...} // clean up
next: {...} // next useEffect
}
}
}
div fiber {
alternate: 自身
stateNode: dom div
type: "div"
elementType: "div"
return: 指向 app fiber
sibling:null
child:指向 p fiber,第一个 child
ref: null
key: null
firstEffect: 需要更新的 fiber
lastEffect: 需要更新的 fiber
nextEffect: null
memoizedProps: 指向 children 组件,react element,不是 fiber,3个 children。p、input、democl
memoizedState: null
prndingProps: 指向 children 组件,react element,不是 fiber,3个 children
}
p fiber {
alternate: 自身
stateNode:dom p
type: "p"
elementType: "p"
return: 指向 div fiber
sibling:指向 input fiber
child:指向 say fiber
ref:{current: f}
key: "test"
fistEffect: null
lastEffect: null
nextEffect: null
memoizedProps: {
children: ["say", {...}, "haaa!"] // react element
className: "test"
id: "test"
onClick: f
style: {color: "#fff"}
},
memoizedState: null
pendingProps: {
children: ["say", {...}, "haaa!"] // react element
className: "test"
id: "test"
onClick: f
style: {color: "#fff"}
}
}
say fiber {
alternate: null
stateNode:text say
type: null
elementType: null
return: 指向 p fiber
sibling:指向 span fiber
child:null
ref:null
key: null
fistEffect: null
lastEffect: null
nextEffect: null
}
...
Democls fiber {
alternate: 自身
stateNode: Democls fiber
type: class Democls
elementType: class Democls
return: 指向 div fiber
sibling: null
child: 指向 div fiber
memoizedState: {count: 1}
memoizedProps: {}
...
updateQueue: {
baseQueue: {
callback: null
next: {...}
pauload: {count: 2} // 还没处理的 update
}
baseState: {count: 1} // 已完成的 update
effects: null
shared: {pending: null}
}
}
-
递归流程
- 从 hostFiber 开始,一直递归第一个子节点
- 直到子节点为空,返回上一个含兄弟节点的父节点,从兄弟节点开始递归第一个子节点
- 直到返回到 hostFiber
-
渲染流程
- 从入口 render 开始
- 先创建 fiberRoot,同时创建 hostFiber
- 然后进入 updateContainer 函数,在这里创建 update,同时通过 enqueueUpdate 函数把 render 入口的 react element 存到 hostfiber.updateQueue.shared.pending.payload
- enqueueUpdate(fiber, update),这个函数专门是将更新存在对应的 fiber 上的
- 设置完 update 就开始继续调度 scheduleWork(fiber, expTime),计算优先级,根据过期时间和优先级,继续不同策略的调度
- expirationTime === Sync,performSyncWorkOnRoot,同步渲染
- ensureRootIsScheduled,performConcurrentWorkOnRoot,异步渲染
- ???同步渲染/异步渲染有什么区别???
- 在 performSyncWorkOnRoot 循环执行 workLoopSync,直到完成,这时证棵 fiber tree 就构建完成了。然后执行 finishSyncRender(root) 进行渲染 dom
- workLoopSync 里根据 react element 循环创建对应的 fiber
- beginWork(current, unitOfWork, expirationTime)
- 根据 fiber.tag 执行不同的创建 fiber 方式
- 比如是 hostRoot,即 hostFiber,会拿到 fiber.updateQueue.shared.pending.payload 上的 react element,即 render 的入口组件。一般是 react element 或者 react 的内置组件。如 react.strict_mode。然后进入调和子组件,reconcileChildren
- 比如是函数组件,直接执行函数组件,执行过程中,会将 hook,props,state 等存到 fiber 上
- 当到达一个树分支的叶子节点,执行 completeUnitOfWork
- 根据 fiber.tag 进行相应的 dom 生成,一般是 hostComponent(即 dom 节点,如 div)/hostText (文本)
- 渲染阶段
- createInstance,创建相应的 dom 节点
- updateFiberProps,存储事件回调
- appendAllChildren,插入到父节点中
- finalizeInitialChildren,初始化,dom 节点的属性,和事件绑定
- markRef,ref 绑定 dom 实例
- createInstance,创建相应的 dom 节点
- 更新阶段
- updateHostComponen,进行属性更新,事件绑定
- beginWork(current, unitOfWork, expirationTime)
- 调和子组件是什么过程?reconcileChildren
- 首次渲染,会直接创建对应的 fiber
- createFiberFromFragment
- createFiberFromElement
- createFiberFromText
- createFiberFromTypeAndProps
- createFiber
- 更新阶段
- reconcileSingleElement,只有一个节点
- 如果不是 fragment,复用该节点的第一个子节点的 fiber,删除其兄弟节点,重置 ref
- 如果是 fragment,删除该节点的兄弟节点,返回字节点
- reconcileSingleTextNode
- 删除该节点的兄弟节点,复用第一个子节点
- reconcileSingleElement,只有一个节点
- 数组节点,即两个新旧数组比较,key 的作用,fiber.index 记录着原数组的长度
- 如果旧的 fiber 存在
- 循环新的数组,通过 key 优化
- 通过第一个 oldfiber 和第一个 element,得到一个新 fiber
- 如果数组里的节点是 string/number,执行 updateTextNode
- 如果是对象,updateElement/updateFragment
- 如果旧的节点为空,创建新的节点
- 循环新的数组,通过 key 优化
- 如果新数组比就数组小,删除旧数组的其他节点
- 如果旧的 fiber 不存在
- 如果旧的数组比新的数组小,直接创建新数组的其他节点的 fiber
- 通过 key 复用 fiber
- 如果旧的 fiber 存在
- 首次渲染,会直接创建对应的 fiber
- 循环执行完 workLoopSync 后,进行 finishSyncRender
- commitBeforeMutationEffects,插入 dom 前,循环遍历 fiber tree 上的 nextEffect,执行一些生命周期函数
- commitBeforeMutationLifeCycles
- 执行 getSnapshotBeforeUpdate
- flushPassiveEffects
- commitHookEffectListUnmount
- commitHookEffectListMount,执行 hook
- commitBeforeMutationLifeCycles
- commitMutationEffects
- commitUpdate
- updateFiberProps
- updateProperties,updateDOMProperties,更新 dom 属性
- commitTextUpdate
- commitPlacement
- insertOrAppendPlacementNode/insertOrAppendPlacementNodeIntoContainer
- 将 fiber.stateNode 插入到 dom root 上
- commitUpdate
- commitLayoutEffects
- commitLifeCycles
- commitHookEffectListMount,hook effect
- componentDidMount/componentDidUpdate
- commitUpdateQueue,执行 render 传入的 callback
- commitAttachRef,绑定 ref
- commitLifeCycles
- commitBeforeMutationEffects,插入 dom 前,循环遍历 fiber tree 上的 nextEffect,执行一些生命周期函数
-
流程回顾
-
// 渲染阶段 scheduleUpdateOnFiber performSyncWorkOnRoot workLoopSync performUnitOfWork beginWork // 通过父 fiber 生成 child element 相应的 fiber,循环完,最终会生成整颗 fiber tree /updateFunctionComponent // 函数组件执行,hook dispatcher 绑定 /updateClassComponent // 类组件执行,相关生命函数执行 /updateHostRoot /updateHostComponent /updateHostText reconcileChildren // child element to fiber completeUnitOfWork completeWork // 给每个 fiber 创建 dom 实例,同时存到对应 fiber.stateNode。循环将子的 dom 实例插入到父的 dom 实例中。然后初始化每个 fiber.statenode 的 dom 属性、事件等 finishSyncRender // 如果是组件,执行相关的生命函数、hook。将 hostfiber.stateNode 插入到 dom root。最后执行 callback
-
hastFiber,对 updateQueue.shared.pending.payload 上的 react element(即 app 组件) 生成对应的 fiber
-
然后通过 app fiber 去 reconcileChildren 调合 children element 的到 children fiber。最终完成整颗 fiber tree
-
在 completeWork 的时候将 instance 存到 fiber.stateNode
-
fiber tree 创建后,会进行 finishSyncRender 生成每个 fiber 对应的 stateNode
-
-
整体流程分为:
- performUnitOfWork = beginWork + reconcilerChildren + completeUnitOfWork
- beginWork: 组件实例化,生命函数
- constructor
- getDeriveStateFromProps
- shouldComponentUpdate
- render
- reconcilerChildren:子 element 对应的 fiber 创建,diff 算法
- completeUnitOfWork:dom 实例化,属性设置/更新,事件注册,dom 插入形成片段
- performUnitOfWork 过程可以被中断/恢复/重来。因为这个阶段没有副作用。会出现生命函数执行多次
- beginWork: 组件实例化,生命函数
- finishSyncRender = commitRoot
- dom 插入,属性更新,生命函数。这个阶段不能中断,含有副作用。
- getSnapshotBeforeUpdate // 可以读到 dom
- componentDidMount
- componentDidUpdate
- dom 插入,属性更新,生命函数。这个阶段不能中断,含有副作用。
- about fiber:
- performUnitOfWork = beginWork + reconcilerChildren + completeUnitOfWork