React 源码分析
- createElement
- workLoop 通过requesetIdleCallback deadline.timeRemaining()
- performUnitWork 拆分任务
- reconcileChildren 构建Fiber
- commit 把fiber 渲染成 真实dom
jsx -> React.createElement
createElement 最后需要放回一个对象
function createElement(type, config, ...children) {
const props = {
...config,
children,
};
return {
type,
props
};
}
举例: 下面是一个jsx, 通过createElement 会变成什么
<div className="border">
<p>标题</p>
<a href="https://bj.58.com">58</a>
</div>
下图就是转成之后的js对象
可以看出对于 text的这种情况不好遍历, 所以改进一下createElement
function createElement(type, config, ...children) {
const props = {
...config,
children: children.map(child => {
return typeof child === 'object' ? child : createTextNode(child)
})
};
return {
type,
props
}
}
function createTextNode(text){
return {
type: "TEXT",
props: {
children : [],
nodeValue: text
}
}
}
分析一下Fiber && reconcileChildren && commit
Fiber 是 js 主要是对虚拟 dom 采用深度优先遍历
分解一下fiber 顺序 child sibling
function workLoop(deadline) {
while( nextUnitOfWork && deadline.timeRemaining() > 1 ){
nextUnitOfWork = performUnitOfWork(nextUnitOfWork) // 拆分小任务
}
if(!nextUnitOfWork && wipRoot){ nextUnitOfWork 是null 的时候 需要 commit
commitRoot()
}
requestIdleCallback(workLoop)
}
requestIdleCallback(workLoop)
分解任务
if(fiber.child){
return fiber.child
}
let nextFiber = fiber;
while(nextFiber){
if(nextFiber.sibling){
return nextFiber.sibling // 表明 如果最后没有sibling的话,以及 return 都没有sibling的话,直接null
}
nextFiber = nextFiber.return
}
遍历之前的虚拟dom变成 fiber结构
let prevSlibling = null
for(let i = 0; i < children.length; i++){
let child = children[i]
let newFiber = {
type: child.type,
props: child.props,
node: null,
return: workInProgressFiber,
effectTag: PLACEMENT
}
if(i == 0){
workInProgressFiber.child = newFiber
}else{
prevSlibling.sibling = newFiber
}
prevSlibling = newFiber
}
最后来commit 一下
if(!fiber){ return }
let parentNodeFiber = fiber.return;
while(!parentNodeFiber.node){
parentNodeFiber = parentNodeFiber.return
}
const parentNode = parentNodeFiber.node
if(fiber.node !== null && fiber.effectTag == PLACEMENT){
parentNode.appendChild(fiber.node)
}
commitWorker(fiber.child)
commitWorker(fiber.sibling)
commit 阶段就是child sibling 一起遍历完成