import { REACT_ELEMENT_TYPE } from '../shared/ReactSymbols' ; import { ReactElement } from '../shared/ReactTypes' ; import { createFiberFromElement , createFiberFromText , createWorkInProgress , } from './ReactFiber' ; import { ChildDeletion , Placement } from './ReactFiberFlags' ; import { Lanes } from './ReactFiberLane' ; import { Fiber } from './ReactInternalTypes' ; import { HostText } from './ReactWorkTags' ; const isArray = Array . isArray ; param shouldTrackSideEffects returns const ChildReconciler = ( shouldTrackSideEffects ) => { const placeSingleChild = ( newFiber ) : Fiber => { if ( shouldTrackSideEffects && newFiber . alternate === null ) { newFiber . flags |= Placement ; } return newFiber ; } ; param returnFiber param currentFirstChild param element param lanes returns const reconcileSingleElement = ( returnFiber , currentFirstChild , element , lanes ) : Fiber => { const key = element . key ; let child = currentFirstChild ; while ( child !== null ) { if ( child . key === key ) { if ( child . elementType === element . type ) { deleteRemainingChildren ( returnFiber , child . sibling ) ; const existing = useFiber ( child , element . props ) ; existing . return = returnFiber ; return existing ; } deleteRemainingChildren ( returnFiber , child ) ; break ; } else { deleteChild ( returnFiber , child ) ; } child = child . sibling ; } const created = createFiberFromElement ( element , returnFiber . mode , lanes ) ; created . return = returnFiber ; return created ; } ; param returnFiber param currentFirstChild returns const deleteRemainingChildren = ( returnFiber , currentFirstChild ) : null => { if ( ! shouldTrackSideEffects ) { return null ; } let childToDelete = currentFirstChild ; while ( childToDelete !== null ) { deleteChild ( returnFiber , childToDelete ) ; childToDelete = childToDelete . sibling ; } return null ; } ; param returnFiber param current param element param lanes returns const updateElement = ( returnFiber , current , element , lanes ) : Fiber => { if ( current !== null ) { if ( current . elementType === element . type ) { const existing = useFiber ( current , element . props ) ; existing . return = returnFiber ; return existing ; } } const created = createFiberFromElement ( element , returnFiber . mode , lanes ) ; created . return = returnFiber ; return created ; } ; const useFiber = ( fiber , pendingProps ) : Fiber => { const clone = createWorkInProgress ( fiber , pendingProps ) ; clone . index = 0 ; clone . sibling = null ; return clone ; } ; param returnFiber param current param textContent param lanes returns const updateTextNode = ( returnFiber , current , textContent , lanes ) => { if ( current === null || current . tag !== HostText ) { const created = createFiberFromText ( textContent , returnFiber . mode , lanes ) ; created . return = returnFiber ; return created ; } else { const existing = useFiber ( current , textContent ) ; existing . return = returnFiber ; return existing ; } } ; param returnFiber param oldFiber param newChild param lanes returns const updateSlot = ( returnFiber , oldFiber , newChild , lanes ) : Fiber | null => { const key = oldFiber ? oldFiber . key : null ; if ( typeof newChild === 'number' || typeof newChild === 'string' ) { if ( key !== null ) { throw new Error ( 'Not Implement' ) ; } return updateTextNode ( returnFiber , oldFiber , '' + newChild , lanes ) ; } if ( typeof newChild === 'object' && newChild !== null ) { switch ( newChild . $$typeof ) { case REACT_ELEMENT_TYPE : { if ( newChild . key === key ) { return updateElement ( returnFiber , oldFiber , newChild , lanes ) ; } else return null ; } } throw new Error ( 'Not Implement' ) ; } if ( newChild == null ) return null ; throw new Error ( 'Invalid Object type' ) ; } ; param newFiber param lastPlacedIndex param newIndex returns const placeChild = ( newFiber , lastPlacedIndex , newIndex ) : number => { newFiber . index = newIndex ; if ( ! shouldTrackSideEffects ) return lastPlacedIndex ; const current = newFiber . alternate ; if ( current !== null ) { const oldIndex = current . index ; if ( oldIndex < lastPlacedIndex ) { newFiber . flags |= Placement ; return lastPlacedIndex ; } else { return oldIndex ; } } else { newFiber . flags |= Placement ; return lastPlacedIndex ; } } ; const createChild = ( returnFiber , newChild , lanes ) : Fiber | null => { if ( typeof newChild === 'string' || typeof newChild === 'number' ) { const created = createFiberFromText ( '' + newChild , returnFiber . mode , lanes ) ; created . return = returnFiber ; return created ; } if ( typeof newChild === 'object' && newChild !== null ) { switch ( newChild . $$typeof ) { case REACT_ELEMENT_TYPE : { const created = createFiberFromElement ( newChild , returnFiber . mode , lanes ) ; created . return = returnFiber ; return created ; } } } if ( newChild == null ) return null ; throw new Error ( 'Not Implement' ) ; } ; param returnFiber param childToDelete returns const deleteChild = ( returnFiber , childToDelete ) : void => { if ( ! shouldTrackSideEffects ) { return ; } const deletions = returnFiber . deletions ; if ( deletions === null ) { returnFiber . deletions = [ childToDelete ] ; returnFiber . flags |= ChildDeletion ; } else { deletions . push ( childToDelete ) ; } } ; param returnFiber param currentFirstChild returns const mapRemainingChildren = ( returnFiber , currentFirstChild ) : Map < string | number , Fiber > => { const existingChildren = new Map ( ) ; let existingChild = currentFirstChild ; while ( existingChild !== null ) { if ( existingChild . key !== null ) { existingChildren . set ( existingChild . key , existingChild ) ; } else { /**
                 * 如果没有显示的设置key，就是用他的index当作key，当然大部分情况下
                 * 这种做法并不能正确的复用他之前的节点，比如以下情况，假设更新前后都没有
                 * 显式的设置key
                 * 更新前: a -> b -> c
                 * 更新后: a -> c
                 * 在这次更新中将b节点删除，此时构建出来的map就为
                 * {
                 *   0 -> a,
                 *   1 -> b,
                 *   2 -> c
                 * }
                 * 而在处理c是实际获得的复用节点为map.get(1)等于b节点
                 * 也就是说本来不用做任何更改的c节点还需要还需要将复用的b节点
                 * 的属性更新至和c一致，这就是为什么说显示的设置唯一的key会
                 * 提高复用节点正确率的原因，当然上述情况如果更新前后都没有
                 * 设置key的话压根不会走进updateFromMap逻辑，在这里这是为了
                 * 方便描述原因
                 */ existingChildren . set ( existingChild . index , existingChild ) ; } existingChild = existingChild . sibling ; } return existingChildren ; } ; param existingChildren param returnFiber param newIdx param newChild param lanes returns const updateFromMap = ( existingChildren , returnFiber , newIdx , newChild , lanes ) : Fiber | null => { if ( typeof newChild === 'string' || typeof newChild === 'number' ) { const matchedFiber = existingChildren . get ( newIdx ) || null ; return updateTextNode ( returnFiber , matchedFiber , '' + newChild , lanes ) ; } if ( typeof newChild === 'object' && newChild !== null ) { switch ( newChild . $$typeof ) { case REACT_ELEMENT_TYPE : { const matchedFiber = existingChildren . get ( newChild . key === null ? newIdx : newChild . key ) ?? null ; return updateElement ( returnFiber , matchedFiber , newChild , lanes ) ; } } throw new Error ( 'Not Implement' ) ; } return null ; } ; const reconcileChildrenArray = ( returnFiber , currentFirstChild , newChildren , lanes ) : Fiber | null => { let resultingFirstChild = null ; let previousNewFiber = null ; let oldFiber = currentFirstChild ; let lastPlacedIndex = 0 ; let newIdx = 0 ; let nextOldFiber = null ; for ( ; oldFiber !== null && newIdx < newChildren . length ; ++ newIdx ) { if ( oldFiber . index > newIdx ) { throw new Error ( 'Not Implement' ) ; } else { nextOldFiber = oldFiber . sibling ; } const newFiber = updateSlot ( returnFiber , oldFiber , newChildren [ newIdx ] , lanes ) ; if ( newFiber === null ) { /**
                 * {type: 'li', key: 1 }   --->   {type: 'li', key: 2 }
                 *                           删除第一个后
                 * {type: 'li', key: 2 }
                 */ if ( oldFiber === null ) { oldFiber = nextOldFiber ; } break ; } if ( shouldTrackSideEffects ) { if ( oldFiber && newFiber . alternate === null ) { /**
                     * 两个位置的元素是匹配的，但是并没有复用现存的fiber,
                     * 所以我们需要把现存的child删掉,新创建的fiber,alternate指向null
                     * 如果使用useFiber复用了节点，则newFiber的alternate会指向current
                     * fiber节点，比如[<div></div>]
                     */ deleteChild ( returnFiber , oldFiber ) ; } } lastPlacedIndex = placeChild ( newFiber , lastPlacedIndex , newIdx ) ; if ( ! previousNewFiber ) { resultingFirstChild = newFiber ; } else { previousNewFiber . sibling = newFiber ; } previousNewFiber = newFiber ; oldFiber = nextOldFiber ; } if ( newIdx === newChildren . length ) { deleteRemainingChildren ( returnFiber , oldFiber ) ; return resultingFirstChild ; } if ( oldFiber === null ) { /**
             * 如果没有现存的节点了，我们可以用这种更快的方法，因为剩下的节点都是待插入的
             */ for ( ; newIdx < newChildren . length ; ++ newIdx ) { const newFiber = createChild ( returnFiber , newChildren [ newIdx ] , lanes ) ; if ( newFiber === null ) continue ; lastPlacedIndex = placeChild ( newFiber , lastPlacedIndex , newIdx ) ; if ( previousNewFiber === null ) { resultingFirstChild = newFiber ; } else { previousNewFiber . sibling = newFiber ; } previousNewFiber = newFiber ; } return resultingFirstChild ; } const existingChildren = mapRemainingChildren ( returnFiber , oldFiber ) ; for ( ; newIdx < newChildren . length ; ++ newIdx ) { const newFiber = updateFromMap ( existingChildren , returnFiber , newIdx , newChildren [ newIdx ] , lanes ) ; if ( newFiber !== null ) { if ( shouldTrackSideEffects ) { if ( newFiber . alternate !== null ) { /**
                         * 更新前: [<div key="1">1<div>, <div key="2">2</div>, <div key="3">3</div>]
                         * 更新后: [<div key="3">3</div>, <div key="1">1<div>]
                         * 在这次更新中子元素的位置发生了变化，而且2还被删除了
                         * 由于第一个newChild进行工作时就会发现，同一位置前后元素
                         * 一个key是1一个是3，所以并没有成功复用节点就会直接break进入这里的updateFromMap逻辑
                         * 所以会更具current fiber节点构建出以下map
                         * {
                         *   1 -> <div key="1">1</div>,
                         *   2 -> <div key="2">2</div>,
                         *   3 -> <div key="3">3</div>,
                         * }
                         * 由于3节点和1节点都成功被复用,所以都会被从map中删除
                         * 所以此时map中还剩下一个2节点，此时就能知道这个2节点
                         * 就是没有被复用的废弃节点待会需要将这些废弃节点标记删除
                         * 这里也就是将2节点标记删除
                         */ existingChildren . delete ( newFiber . key === null ? newIdx : newFiber . key ) ; } } lastPlacedIndex = placeChild ( newFiber , lastPlacedIndex , newIdx ) ; if ( previousNewFiber === null ) { resultingFirstChild = newFiber ; } else { previousNewFiber . sibling = newFiber ; } previousNewFiber = newFiber ; } } if ( shouldTrackSideEffects ) { existingChildren . forEach ( ( child ) => deleteChild ( returnFiber , child ) ) ; } return resultingFirstChild ; } ; param returnFiber param currentFirstChild param newChild param lanes returns const reconcileChildFibers = ( returnFiber , currentFirstChild , newChild , lanes ) : Fiber | null => { const isObject = typeof newChild === 'object' && newChild !== null ; if ( isObject ) { switch ( newChild . $$typeof ) { case REACT_ELEMENT_TYPE : { return placeSingleChild ( reconcileSingleElement ( returnFiber , currentFirstChild , newChild , lanes ) ) ; } } } if ( isArray ( newChild ) ) { return reconcileChildrenArray ( returnFiber , currentFirstChild , newChild , lanes ) ; } if ( typeof newChild === 'string' || typeof newChild === 'number' ) { throw new Error ( 'Not Implement' ) ; } return deleteRemainingChildren ( returnFiber , currentFirstChild ) ; } ; return reconcileChildFibers ; } ; param current param workInProgress returns export const cloneChildFibers = ( current , workInProgress ) : void => { if ( workInProgress . child === null ) return ; let currentChild = workInProgress . child ; let newChild = createWorkInProgress ( currentChild , currentChild . pendingProps ) ; workInProgress . child = newChild ; newChild . return = workInProgress ; /**
     * 只复制一层，也就是只复制子节点，其他的不管
     */ while ( currentChild . sibling !== null ) { currentChild = currentChild . sibling ; newChild = newChild . sibling = createWorkInProgress ( currentChild , currentChild . pendingProps ) ; newChild . return = workInProgress ; } newChild . sibling = null ; } ; export const mountChildFibers = ChildReconciler ( false ) ; export const reconcileChildFibers = ChildReconciler ( true ) ;