Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

react v16.13.1 源码阅读 #20

Open
Jarweb opened this issue Aug 25, 2020 · 0 comments
Open

react v16.13.1 源码阅读 #20

Jarweb opened this issue Aug 25, 2020 · 0 comments

Comments

@Jarweb
Copy link
Owner

Jarweb commented Aug 25, 2020

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
            • finishSyncRender,对 fiber node 生成 dom 片段,插入 dom

              • commitRoot,commitRootImpl,

              • commitBeforeMutationEffects

                • commitBeforeMutationLifeCycles
                • flushPassiveEffects
                  • commitPassiveHookEffects
                    • commitHookEffectListUnmount,执行 hook 的 clean。hook 存在 fiber node 的 lasteffect/firsteffect 上
                    • commitHookEffectListMount,执行 hook 的 create
                  • flushSyncCallbackQueue
              • 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 来显示隐藏节点
                • commitDeletion
                  • unmountHostComponents
                    • commitNestedUnmounts
                    • commitUnmount
                    • removeChildFromContainer
                    • removeChild
                    • commitUnmount
                    • onCommitUnmount
                    • safelyCallDestroy,hook destory 清除副作用
                    • safelyDetachRef,重置 ref
                    • safelyCallComponentWillUnmount,执行 unmount 函数
                  • detachFiber,重置 fiber node 的 alternate 属性
              • 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
                • commitAttachRef
                  • 给 fiber.ref 赋值 fiber.stateNode, stateNode 就是 dom 实例
              • flushSyncCallbackQueue,执行 render 传入的 callback

  • 总结

    • beginWork 阶段
      • 组件实例化,执行部分生命周期函数
      • reconcileChildren 调和子组件,生成子组件的 fiber node,子组件实例化,执行自身生命周期函数
      • completeUnitOfWork 对 html tag/text 节点进行 dom 创建,属性挂载/事件绑定
    • finishSyncRender 阶段
      • dom 片段生成,执行剩余生命周期函数

更新阶段

  • 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
  • 总结

    • 触发更新动作,创建 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 进行调度执行

image-20200602230333548

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

image-20200603132011686

  • 递归流程

    • 从 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 实例
        • 更新阶段
          • updateHostComponen,进行属性更新,事件绑定
    • 调和子组件是什么过程?reconcileChildren
      • 首次渲染,会直接创建对应的 fiber
        • createFiberFromFragment
        • createFiberFromElement
        • createFiberFromText
        • createFiberFromTypeAndProps
        • createFiber
      • 更新阶段
        • reconcileSingleElement,只有一个节点
          • 如果不是 fragment,复用该节点的第一个子节点的 fiber,删除其兄弟节点,重置 ref
          • 如果是 fragment,删除该节点的兄弟节点,返回字节点
        • reconcileSingleTextNode
          • 删除该节点的兄弟节点,复用第一个子节点
      • 数组节点,即两个新旧数组比较,key 的作用,fiber.index 记录着原数组的长度
        • 如果旧的 fiber 存在
          • 循环新的数组,通过 key 优化
            • 通过第一个 oldfiber 和第一个 element,得到一个新 fiber
            • 如果数组里的节点是 string/number,执行 updateTextNode
            • 如果是对象,updateElement/updateFragment
            • 如果旧的节点为空,创建新的节点
        • 如果新数组比就数组小,删除旧数组的其他节点
        • 如果旧的 fiber 不存在
          • 如果旧的数组比新的数组小,直接创建新数组的其他节点的 fiber
        • 通过 key 复用 fiber
    • 循环执行完 workLoopSync 后,进行 finishSyncRender
      • commitBeforeMutationEffects,插入 dom 前,循环遍历 fiber tree 上的 nextEffect,执行一些生命周期函数
        • commitBeforeMutationLifeCycles
          • 执行 getSnapshotBeforeUpdate
        • flushPassiveEffects
          • commitHookEffectListUnmount
          • commitHookEffectListMount,执行 hook
      • commitMutationEffects
        • commitUpdate
          • updateFiberProps
          • updateProperties,updateDOMProperties,更新 dom 属性
        • commitTextUpdate
        • commitPlacement
          • insertOrAppendPlacementNode/insertOrAppendPlacementNodeIntoContainer
          • 将 fiber.stateNode 插入到 dom root 上
      • commitLayoutEffects
        • commitLifeCycles
          • commitHookEffectListMount,hook effect
          • componentDidMount/componentDidUpdate
          • commitUpdateQueue,执行 render 传入的 callback
        • commitAttachRef,绑定 ref
  • 流程回顾

    • // 渲染阶段
      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 过程可以被中断/恢复/重来。因为这个阶段没有副作用。会出现生命函数执行多次
    • finishSyncRender = commitRoot
      • dom 插入,属性更新,生命函数。这个阶段不能中断,含有副作用。
        • getSnapshotBeforeUpdate // 可以读到 dom
        • componentDidMount
        • componentDidUpdate
    • about fiber:

概览图

image-20200824154805801

image-20200824182042035

image-20200824154901823

image-20200824155010668

image-20200824155029271

image-20200824175834090

image-20200825125206187

image-20200825125236968

image-20200825125308903

image-20200825131424774

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant