We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
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
在下一个 state 或 props 更新时,render() 函数将会返回一个不同的 React 元素树。接下来 React 将会找出如何高效地更新 UI 来匹配最近时刻的 React 元素树。
state
props
render()
React
UI
可以通过指定 key 属性,来指示哪些元素可以保持稳定。 props.key 也对应 fiber.key
key
props.key
fiber.key
render () { return ( <div> {this.state.arr.map((item, idx) => { return <p key={item.name}>{item.name}</p> })} </div> ) }
当为了创建/重用 childFiber 而执行 beginWork ,并在根据 workInProgress.tag 来更新 Component 时,会用到 reconcileChildren 来对所有新旧子 fiber 进行一致性比较
childFiber
beginWork
workInProgress.tag
Component
reconcileChildren
fiber
reconcileChildren 需要比较是否是第一次生成子 fiber ,是则 currentChild = null; 否则比较后更新后 fiber。如下代码所示,通过 workInProgress.alternate 来判断是否是第一次创建子 fiber。 mountChildFibers 和 reconcileChildFibers 执行函数相同,只是参数不同
currentChild = null;
workInProgress.alternate
mountChildFibers
reconcileChildFibers
var reconcileChildFibers = ChildReconciler(true); var mountChildFibers = ChildReconciler(false); function reconcileChildrenAtExpirationTime(current, workInProgress, nextChildren) { var current = workInProgress.alternate; // 第一次创建子fiber为null if (current === null) { workInProgress.child = mountChildFibers(workInProgress, null, nextChildren); } else { workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren); } }
class App extends Component { constructor (props) { super(props); this.state = { arr: [ {name: '1'}, {name: '2'} ] } } handleClick = (e) => { let arr = this.state.arr this.setState({ arr: [{name: '4'}, arr[1], {name: '3'}, arr[0]] }) } render() { return ( <div className="App"> <button onClick={this.handleClick} className="aaa">click</button> <div> {this.state.arr.map((item, idx) => { return <p key={item.name}>{item.name}</p> })} </div> </div> ); } }
上述代码,会渲染带有 key的子节点
作用:在更新时, React 会用这个 key 去比较原来的树的子节点和之后的树的子节点
在点击 button 之后,会执行事件回调函数 handleClick ,在此函数中有 setState 操作,会将更新队列挂载到 instance 上,而后在 updateClassInstance 时,将改变 instance.state
button
handleClick
setState
instance
updateClassInstance
instance.state
var newState = processUpdateQueue(); instance.state = newState;
改变 instance.state主要是为了在 render 方法中用 this.state 来取得。
render
this.state
newChild
updateClassComponent
element
var newChild = instance.render();
updateHostComponent
props.children
var newChild = nextProps.children;
接下来,就拿出代码来深度剖析,reconcileChildFibers 是决定用那种方式对 newChild 和 oldChild 进行 Reconciliation。
oldChild
Reconciliation
function reconcileChildFibers(returnFiber, currentFirstChild, newChild, expirationTime) { // currentFirstChild指第一个子fiber if (typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null) { newChild = newChild.props.children; } // Handle object types var isObject = typeof newChild === 'object' && newChild !== null; if (isObject) { switch (newChild.$$typeof) { case REACT_ELEMENT_TYPE: return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, expirationTime)); // 创建child fiber case REACT_CALL_TYPE: return placeSingleChild(reconcileSingleCall(returnFiber, currentFirstChild, newChild, expirationTime)); case REACT_RETURN_TYPE: return placeSingleChild(reconcileSingleReturn(returnFiber, currentFirstChild, newChild, expirationTime)); case REACT_PORTAL_TYPE: return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, expirationTime)); } } if (typeof newChild === 'string' || typeof newChild === 'number') { return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, expirationTime)); } if (isArray$1(newChild)) { // 如果新的child是数组,这里的newChild是nextProps.children return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, expirationTime); } if (getIteratorFn(newChild)) { return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, expirationTime); } }
如果是第一次生成 childFiber ,则直接根据元素类型( element.type )来创建 childFiber。
element.type
如果是更新,则按序比较新旧孩子 fiber的 key ,即 childFiber-childFiber.sibling-childFiber.sibling.sibling....。如果有多个孩子 fiber ,将 key 不一样的 fiber 标记为删除,key 一样的则重用。
childFiber-childFiber.sibling-childFiber.sibling.sibling....
fiber.effectTag = Deletion; returnFiber.firstEffect = returnFiber.lastEffect = fiber;
新旧 children 对比,对于 key 相同的 fiber ,采取 updateSlot 更新措施(如果 key 相同,则更新 fiber 并返回,否则返回 null)。
children
updateSlot
null
主要工作是将 child 用 sibling 连起来,最终返回第一个 child。
child
sibling
遍历新 children:
var existingChildren = mapRemainingChildren(returnFiber, oldFiber);
mapRemainingChildren 是为了将旧的 children 以 key 为键存入 map 中
mapRemainingChildren
map
var _newFiber2 = updateFromMap(existingChildren, returnFiber, newIdx, newChildren[newIdx], expirationTime);
updateFromMap 为了返回创建或者重用的 fiber
updateFromMap
lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx);
placeChild 为新的 newFiber 添加 index 索引,并置 effectTag 为 Placement 。如果 newFiber 是新创建的,或者 newFiber 是重用的但是位置发生了改变,这两种情况都会置 effectTag 为 Placement,否则不设置 effectTag
placeChild
newFiber
index
effectTag
Placement
由此我们就能看到 Reconciliation 的作用了
The text was updated successfully, but these errors were encountered:
No branches or pull requests
在下一个
state
或props
更新时,render()
函数将会返回一个不同的React
元素树。接下来React
将会找出如何高效地更新UI
来匹配最近时刻的React
元素树。可以通过指定
key
属性,来指示哪些元素可以保持稳定。props.key
也对应fiber.key
一致性比较的几个算法
一致性比较的使用
当为了创建/重用
childFiber
而执行beginWork
,并在根据workInProgress.tag
来更新Component
时,会用到reconcileChildren
来对所有新旧子fiber
进行一致性比较reconcileChildren
需要比较是否是第一次生成子fiber
,是则currentChild = null;
否则比较后更新后fiber
。如下代码所示,通过workInProgress.alternate
来判断是否是第一次创建子fiber
。mountChildFibers
和reconcileChildFibers
执行函数相同,只是参数不同举例分析
上述代码,会渲染带有
key
的子节点作用:在更新时,
React
会用这个key
去比较原来的树的子节点和之后的树的子节点在点击
button
之后,会执行事件回调函数handleClick
,在此函数中有setState
操作,会将更新队列挂载到instance
上,而后在updateClassInstance
时,将改变instance.state
改变
instance.state
主要是为了在render
方法中用this.state
来取得。reconcileChildFibers
中重要的newChild
参数updateClassComponent
中,newChild
是render
方法返回后的element
var newChild = instance.render();
updateHostComponent
中,newChild
是props.children
(如果props.children
不是文本字符串)var newChild = nextProps.children;
接下来,就拿出代码来深度剖析,
reconcileChildFibers
是决定用那种方式对newChild
和oldChild
进行Reconciliation
。如果是第一次生成
childFiber
,则直接根据元素类型(element.type
)来创建childFiber
。如果是更新,则按序比较新旧孩子
fiber
的key
,即childFiber-childFiber.sibling-childFiber.sibling.sibling....
。如果有多个孩子fiber
,将key
不一样的fiber
标记为删除,key
一样的则重用。新旧
children
对比,对于key
相同的fiber
,采取updateSlot
更新措施(如果key
相同,则更新fiber
并返回,否则返回null
)。主要工作是将
child
用sibling
连起来,最终返回第一个child
。遍历新
children
:mapRemainingChildren
是为了将旧的children
以key
为键存入map
中updateFromMap
为了返回创建或者重用的fiber
placeChild
为新的newFiber
添加index
索引,并置effectTag
为Placement
。如果newFiber
是新创建的,或者newFiber
是重用的但是位置发生了改变,这两种情况都会置effectTag
为Placement
,否则不设置effectTag
由此我们就能看到
Reconciliation
的作用了作用
fiber
fiber
用sibling
连接起来fiber
的effectTag
The text was updated successfully, but these errors were encountered: