一 引沿
Fiber 架构是React16中引入的新概念,目标就是解决大型 React 利用卡顿,React在遍历更新每一个节点的时候都不是用的实在DOM,都是采纳虚构DOM,所以能够了解成fiber就是React的虚构DOM,更新Fiber的过程叫做和谐,每一个fiber都能够作为一个执行单元来解决,所以每一个 fiber 能够依据本身的过期工夫expirationTime,来判断是否还有空间工夫执行更新,如果没有工夫更新,就要把主动权交给浏览器去渲染,做一些动画,重排( reflow ),重绘 repaints 之类的事件,这样就能给用户感觉不是很卡。
二 什么是和谐
和谐是一种算法,就是React比照新老虚构DOM的过程,以决定须要更新哪一部分。
三 什么是Filber
Fiber的目标是为了让React充分利用调度,以便做到如下几点:
- 暂停工作,稍后再回来
- 优先思考不同类型的工作
- 重用以前实现的工作
- 如果不再须要,则停止工作
为了实现下面的要求,咱们须要把工作拆分成一个个可执行的单元,这些可执行的单元就叫做一个Fiber,一个Fiber就代表一个可执行的单元。
一个Fiber就是一个一般的JS对象,蕴含一些组件的相干信息。
function FiberNode(){ this.tag = tag; // fiber 标签 证实是什么类型fiber。 this.key = key; // key和谐子节点时候用到。 this.type = null; // dom元素是对应的元素类型,比方div,组件指向组件对应的类或者函数。 this.stateNode = null; // 指向对应的实在dom元素,类组件指向组件实例,能够被ref获取。 this.return = null; // 指向父级fiber this.child = null; // 指向子级fiber this.sibling = null; // 指向兄弟fiber this.index = 0; // 索引 this.ref = null; // ref指向,ref函数,或者ref对象。 this.pendingProps = pendingProps;// 在一次更新中,代表element创立 this.memoizedProps = null; // 记录上一次更新结束后的props this.updateQueue = null; // 类组件寄存setState更新队列,函数组件寄存 this.memoizedState = null; // 类组件保留state信息,函数组件保留hooks信息,dom元素为null this.dependencies = null; // context或是工夫的依赖项 this.mode = mode; //形容fiber树的模式,比方 ConcurrentMode 模式 this.effectTag = NoEffect; // effect标签,用于收集effectList this.nextEffect = null; // 指向下一个effect this.firstEffect = null; // 第一个effect this.lastEffect = null; // 最初一个effect this.expirationTime = NoWork; // 通过不同过期工夫,判断工作是否过期, 在v17版本用lane示意。 this.alternate = null; //双缓存树,指向缓存的fiber。更新阶段,两颗树相互交替。}
type 就是react的元素类型
export const FunctionComponent = 0; // 对应函数组件export const ClassComponent = 1; // 对应的类组件export const IndeterminateComponent = 2; // 初始化的时候不晓得是函数组件还是类组件 export const HostRoot = 3; // Root Fiber 能够了解为跟元素 , 通过reactDom.render()产生的根元素export const HostPortal = 4; // 对应 ReactDOM.createPortal 产生的 Portal export const HostComponent = 5; // dom 元素 比方 <div>export const HostText = 6; // 文本节点export const Fragment = 7; // 对应 <React.Fragment> export const Mode = 8; // 对应 <React.StrictMode> export const ContextConsumer = 9; // 对应 <Context.Consumer>export const ContextProvider = 10; // 对应 <Context.Provider>export const ForwardRef = 11; // 对应 React.ForwardRefexport const Profiler = 12; // 对应 <Profiler/ >export const SuspenseComponent = 13; // 对应 <Suspense>export const MemoComponent = 14; // 对应 React.memo 返回的组件
比方元素构造如下:
export default class Parent extends React.Component{ render(){ return <div> <h1>hello,world</h1> <Child /> </div> }}function Child() { return <p>child</p>}
对应的Filber构造如下:
有了下面的概念后咱们就本人实现一个Fiber的更新机制
四 实现和谐的过程
咱们通过渲染一段jsx来阐明React的和谐过程,也就是咱们要手写实现ReactDOM.render()
const jsx = ( <div className="border"> <h1>hello</h1> <a href="https://www.reactjs.org/">React</a> </div>)ReactDOM.render( jsx, document.getElementById('root'));
1. 创立FiberRoot
react-dom.js
function createFiberRoot(element, container){ return { type: container.nodeName.toLocaleLowerCase(), props: { children: element }, stateNode: container }}function render(element, container) { const FibreRoot = createFiberRoot(element, container) scheduleUpdateOnFiber(FibreRoot)}export default { render }
参考React实战视频解说:进入学习
2. render阶段
和谐的外围是render和commit,本文不讲调度过程,咱们会简略的用requestIdleCallback代替React的调度过程。
ReactFiberWorkloop.js
let wipRoot = null // work in progresslet nextUnitOfwork = null // 下一个fiber节点export function scheduleUpdateOnFiber(fiber) { wipRoot = fiber nextUnitOfwork = fiber}function workLoop(IdleDeadline) { while(nextUnitOfwork && IdleDeadline.timeRemaining() > 0) { nextUnitOfwork = performUnitOfWork(nextUnitOfwork) }}function performUnitOfWork() {}requestIdleCallback(workLoop)
每一个 fiber 能够看作一个执行的单元,在和谐过程中,每一个产生更新的 fiber 都会作为一次 workInProgress 。那么 workLoop 就是执行每一个单元的调度器,如果渲染没有被中断,那么 workLoop 会遍历一遍 fiber 树
performUnitOfWork 包含两个阶段:
- 是向下和谐的过程,就是由 fiberRoot 依照 child 指针逐层向下和谐,期间会执行函数组件,实例类组件,diff 和谐子节点
- 是向上归并的过程,如果有兄弟节点,会返回 sibling兄弟,没有返回 return 父级,始终返回到 fiebrRoot
这么一上一下,形成了整个 fiber 树的和谐。
import { updateHostComponent } from './ReactFiberReconciler'function performUnitOfWork(wip) { // 1. 更新wip const { type } = wip if (isStr(type)) { // type是string,更新一般元素节点 updateHostComponent(wip) } else if (isFn(type)) { // ... } // 2. 返回下一个要更新的工作 深度优先遍历 if (wip.child) { return wip.child } let next = wip while(next) { if (next.sibling) { return next.sibling } next = next.return } return null}
依据type类型辨别是FunctionComponent/ClassComponent/HostComponent/...
本文中只解决HostComponent类型,其余类型的解决能够看文末的残缺代码链接。
ReactFiberReconciler.js
import { createFiber } from './createFiber'export function updateHostComponent(wip) { if (!wip.stateNode) { wip.stateNode = document.createElement(wip.type); updateNode(wip.stateNode, wip.props); } // 和谐子节点 reconcileChildren(wip, wip.props.children);}function reconcileChildren(returnFiber, children) { if (isStr(children)) { return } const newChildren = isArray(children) ? children : [children]; let previousNewFiber = null for(let i = 0; i < newChildren.length; i++) { const newChild = newChildren[i]; const newFiber = createFiber(newChild, returnFiber) if (previousNewFiber === null) { returnFiber.child = newFiber } else { previousNewFiber.sibling = newFiber } previousNewFiber = newFiber }}function updateNode(node, nextVal) { Object.keys(nextVal).forEach((k) => { if (k === "children") { if (isStringOrNumber(nextVal[k])) { node.textContent = nextVal[k]; } } else { node[k] = nextVal[k]; } });}
createFiber.js
export function createFiber(vnode, returnFiber) { const newFiber = { type: vnode.type, // 标记节点类型 key: vnode.key, // 标记节点在以后层级下的唯一性 props: vnode.props, // 属性 stateNode: null, // 如果组件是原生标签则是dom节点,如果是类组件则是类实例 child: null, // 第一个子节点 return: returnFiber,// 父节点 sibling: null, // 下一个兄弟节点 }; return newFiber;}
至此曾经实现了render阶段,上面是commit阶段,commit阶段就是根据Fiber构造操作DOM
function workLoop(IdleDeadline) { while(nextUnitOfwork && IdleDeadline.timeRemaining() > 0) { nextUnitOfwork = performUnitOfWork(nextUnitOfwork) } // commit if (!nextUnitOfwork && wipRoot) { commitRoot(); }}function commitRoot() { commitWorker(wipRoot.child) wipRoot = null;}function commitWorker(wip) { if (!wip) { return } // 1. 提交本人 const { stateNode } = wip let parentNode = wip.return.stateNode if (stateNode) { parentNode.appendChild(stateNode); } // 2. 提交子节点 commitWorker(wip.child); // 3. 提交兄弟节点 commitWorker(wip.sibling);}
五 总结
- Fiber构造,Fiber的生成过程。
- 和谐过程,以及 render 和 commit 两大阶段。