12. 手写迷你 react(短小精悍就是我)
发布于: 2021 年 03 月 04 日
人人都能读懂的 react 源码解析(大厂高薪必备)
12.手写迷你 react(短小精悍就是我)
视频课程 &调试 demos
视频课程的目的是为了快速掌握 react 源码运行的过程和 react 中的 scheduler、reconciler、renderer、fiber 等,并且详细 debug 源码和分析,过程更清晰。
视频课程:进入课程
demos:demo
课程结构:
迷你 react 和真正的源码有哪些区别呢
在 render 阶段我们遍历了整颗 Fiber 树,在源码中如果节点什么都没改变会命中优化的逻辑,然后跳过这个节点的遍历
commit 我们也遍历了整颗 Fiber 树,源码中只遍历带有 effect 的 Fiber 节点,也就是遍历 effectList
每次遍历的时候我们都是新建节点,源码中某些条件会复用节点
没有用到优先级
第一步:渲染器和入口函数
const React = { createElement, render, } const container = document.getElementById("root") const updateValue = e => { rerender(e.target.value) } const rerender = value => { const element = ( <div> <input onInput={updateValue} value={value} /> <h2>Hello {value}</h2> </div> ) React.render(element, container) } rerender("World")
复制代码
第二步:创建 dom 节点函数
function createElement(type, props, ...children) {//创建element return { type, props: { ...props, children: children.map(child => typeof child === "object" ? child : createTextElement(child) ), }, } } function createTextElement(text) {//创建text类型 return { type: "TEXT_ELEMENT", props: { nodeValue: text, children: [], }, } } function createDom(fiber) {//创建dom const dom = fiber.type == "TEXT_ELEMENT" ? document.createTextNode("") : document.createElement(fiber.type) updateDom(dom, {}, fiber.props) return dom }
复制代码
第三步:更新节点函数
const isEvent = key => key.startsWith("on") const isProperty = key => key !== "children" && !isEvent(key) const isNew = (prev, next) => key => prev[key] !== next[key] const isGone = (prev, next) => key => !(key in next) function updateDom(dom, prevProps, nextProps) {//更新节点属性
//删除老的事件 Object.keys(prevProps) .filter(isEvent) .filter( key => !(key in nextProps) || isNew(prevProps, nextProps)(key) ) .forEach(name => { const eventType = name .toLowerCase() .substring(2) dom.removeEventListener( eventType, prevProps[name] ) }) // 删除旧属性 Object.keys(prevProps) .filter(isProperty) .filter(isGone(prevProps, nextProps)) .forEach(name => { dom[name] = "" }) // 设置新属性 Object.keys(nextProps) .filter(isProperty) .filter(isNew(prevProps, nextProps)) .forEach(name => { dom[name] = nextProps[name] }) // 增加新事件 Object.keys(nextProps) .filter(isEvent) .filter(isNew(prevProps, nextProps)) .forEach(name => { const eventType = name .toLowerCase() .substring(2) dom.addEventListener( eventType, nextProps[name] ) }) }
复制代码
第四步:render 阶段
function performUnitOfWork(fiber) {//render阶段 if (!fiber.dom) { fiber.dom = createDom(fiber) } const elements = fiber.props.children reconcileChildren(fiber, elements) if (fiber.child) { return fiber.child } let nextFiber = fiber while (nextFiber) { if (nextFiber.sibling) { return nextFiber.sibling } nextFiber = nextFiber.parent } } function reconcileChildren(wipFiber, elements) {//reconcile节点 let index = 0 let oldFiber = wipFiber.alternate && wipFiber.alternate.child let prevSibling = null while ( index < elements.length || oldFiber != null ) { const element = elements[index] let newFiber = null const sameType = oldFiber && element && element.type == oldFiber.type if (sameType) { newFiber = { type: oldFiber.type, props: element.props, dom: oldFiber.dom, parent: wipFiber, alternate: oldFiber, effectTag: "UPDATE", } } if (element && !sameType) { newFiber = { type: element.type, props: element.props, dom: null, parent: wipFiber, alternate: null, effectTag: "PLACEMENT", } } if (oldFiber && !sameType) { oldFiber.effectTag = "DELETION" deletions.push(oldFiber) } if (oldFiber) { oldFiber = oldFiber.sibling } if (index === 0) { wipFiber.child = newFiber } else if (element) { prevSibling.sibling = newFiber } prevSibling = newFiber index++ } }
复制代码
第五步:commit 阶段
function commitRoot() {//commit阶段 deletions.forEach(commitWork) commitWork(wipRoot.child) currentRoot = wipRoot wipRoot = null } function commitWork(fiber) {//操作真实dom if (!fiber) { return } const domParent = fiber.parent.dom if ( fiber.effectTag === "PLACEMENT" && fiber.dom != null ) { domParent.appendChild(fiber.dom) } else if ( fiber.effectTag === "UPDATE" && fiber.dom != null ) { updateDom( fiber.dom, fiber.alternate.props, fiber.props ) } else if (fiber.effectTag === "DELETION") { domParent.removeChild(fiber.dom) } commitWork(fiber.child) commitWork(fiber.sibling) }
复制代码
第六步:开始调度
function render(element, container) {//渲染开始的入口 wipRoot = { dom: container, props: { children: [element], }, alternate: currentRoot, } deletions = [] nextUnitOfWork = wipRoot } let nextUnitOfWork = null let currentRoot = null let wipRoot = null let deletions = null function workLoop(deadline) {//调度函数 let shouldYield = false while (nextUnitOfWork && !shouldYield) { nextUnitOfWork = performUnitOfWork(//render阶段 nextUnitOfWork ) shouldYield = deadline.timeRemaining() < 1 } if (!nextUnitOfWork && wipRoot) { commitRoot()//commit阶段 } requestIdleCallback(workLoop)//空闲调度 } requestIdleCallback(workLoop)
复制代码
完整代码
function createElement(type, props, ...children) {//创建element return { type, props: { ...props, children: children.map(child => typeof child === "object" ? child : createTextElement(child) ), }, } } function createTextElement(text) {//创建text类型 return { type: "TEXT_ELEMENT", props: { nodeValue: text, children: [], }, } } function createDom(fiber) {//创建dom const dom = fiber.type == "TEXT_ELEMENT" ? document.createTextNode("") : document.createElement(fiber.type) updateDom(dom, {}, fiber.props) return dom } const isEvent = key => key.startsWith("on") const isProperty = key => key !== "children" && !isEvent(key) const isNew = (prev, next) => key => prev[key] !== next[key] const isGone = (prev, next) => key => !(key in next) function updateDom(dom, prevProps, nextProps) {//更新节点属性
//删除老的事件 Object.keys(prevProps) .filter(isEvent) .filter( key => !(key in nextProps) || isNew(prevProps, nextProps)(key) ) .forEach(name => { const eventType = name .toLowerCase() .substring(2) dom.removeEventListener( eventType, prevProps[name] ) }) // 删除旧属性 Object.keys(prevProps) .filter(isProperty) .filter(isGone(prevProps, nextProps)) .forEach(name => { dom[name] = "" }) // 设置新属性 Object.keys(nextProps) .filter(isProperty) .filter(isNew(prevProps, nextProps)) .forEach(name => { dom[name] = nextProps[name] }) // 增加新事件 Object.keys(nextProps) .filter(isEvent) .filter(isNew(prevProps, nextProps)) .forEach(name => { const eventType = name .toLowerCase() .substring(2) dom.addEventListener( eventType, nextProps[name] ) }) } function commitRoot() {//commit阶段 deletions.forEach(commitWork) commitWork(wipRoot.child) currentRoot = wipRoot wipRoot = null } function commitWork(fiber) {//操作真实dom if (!fiber) { return } const domParent = fiber.parent.dom if ( fiber.effectTag === "PLACEMENT" && fiber.dom != null ) { domParent.appendChild(fiber.dom) } else if ( fiber.effectTag === "UPDATE" && fiber.dom != null ) { updateDom( fiber.dom, fiber.alternate.props, fiber.props ) } else if (fiber.effectTag === "DELETION") { domParent.removeChild(fiber.dom) } commitWork(fiber.child) commitWork(fiber.sibling) } function render(element, container) {//渲染开始的入口 wipRoot = { dom: container, props: { children: [element], }, alternate: currentRoot, } deletions = [] nextUnitOfWork = wipRoot } let nextUnitOfWork = null let currentRoot = null let wipRoot = null let deletions = null function workLoop(deadline) {//调度函数 let shouldYield = false while (nextUnitOfWork && !shouldYield) { nextUnitOfWork = performUnitOfWork(//render阶段 nextUnitOfWork ) shouldYield = deadline.timeRemaining() < 1 } if (!nextUnitOfWork && wipRoot) { commitRoot()//commit阶段 } requestIdleCallback(workLoop)//空闲调度 } requestIdleCallback(workLoop) function performUnitOfWork(fiber) {//render阶段 if (!fiber.dom) { fiber.dom = createDom(fiber) } const elements = fiber.props.children reconcileChildren(fiber, elements) if (fiber.child) { return fiber.child } let nextFiber = fiber while (nextFiber) { if (nextFiber.sibling) { return nextFiber.sibling } nextFiber = nextFiber.parent } } function reconcileChildren(wipFiber, elements) {//reconcile节点 let index = 0 let oldFiber = wipFiber.alternate && wipFiber.alternate.child let prevSibling = null while ( index < elements.length || oldFiber != null ) { const element = elements[index] let newFiber = null const sameType = oldFiber && element && element.type == oldFiber.type if (sameType) { newFiber = { type: oldFiber.type, props: element.props, dom: oldFiber.dom, parent: wipFiber, alternate: oldFiber, effectTag: "UPDATE", } } if (element && !sameType) { newFiber = { type: element.type, props: element.props, dom: null, parent: wipFiber, alternate: null, effectTag: "PLACEMENT", } } if (oldFiber && !sameType) { oldFiber.effectTag = "DELETION" deletions.push(oldFiber) } if (oldFiber) { oldFiber = oldFiber.sibling } if (index === 0) { wipFiber.child = newFiber } else if (element) { prevSibling.sibling = newFiber } prevSibling = newFiber index++ } } const React = { createElement, render, } const container = document.getElementById("root") const updateValue = e => { rerender(e.target.value) } const rerender = value => { const element = ( <div> <input onInput={updateValue} value={value} /> <h2>Hello {value}</h2> </div> ) React.render(element, container) } rerender("World")复制代码
划线
评论
复制
发布于: 2021 年 03 月 04 日阅读数: 9
全栈潇晨
关注
还未添加个人签名 2021.02.17 加入
还未添加个人简介











评论