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 加入
还未添加个人简介











 
    
评论