• 12.手写迷你react(短小精悍就是我)


    人人都能读懂的react源码解析(大厂高薪必备)

    12.手写迷你react(短小精悍就是我)

    视频课程&调试demos

    ​ 视频课程的目的是为了快速掌握react源码运行的过程和react中的scheduler、reconciler、renderer、fiber等,并且详细debug源码和分析,过程更清晰。

    ​ 视频课程:进入课程

    ​ demos:demo

    课程结构:

    1. 开篇(听说你还在艰难的啃react源码)
    2. react心智模型(来来来,让大脑有react思维吧)
    3. Fiber(我是在内存中的dom)
    4. 从legacy或concurrent开始(从入口开始,然后让我们奔向未来)
    5. state更新流程(setState里到底发生了什么)
    6. render阶段(厉害了,我有创建Fiber的技能)
    7. commit阶段(听说renderer帮我们打好标记了,映射真实节点吧)
    8. diff算法(妈妈再也不担心我的diff面试了)
    9. hooks源码(想知道Function Component是怎样保存状态的嘛)
    10. scheduler&lane模型(来看看任务是暂停、继续和插队的)
    11. concurrent mode(并发模式是什么样的)
    12. 手写迷你react(短小精悍就是我)

    迷你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")
    
  • 相关阅读:
    推荐算法学习资料
    imsdroid 学习(初认识)
    从网易新闻看离线阅读的实现思路
    关于PullToRefreshView bug 的修复
    Android Log日志的封装类,显示类名以及行号,快速定位
    Android Sqlite数据库版本升级管理初探
    《围观啦》发布了!!!!!!!
    单本书阅读,android客户端
    Android P2P语音通话实现(思路探讨)
    HTTP协议基础
  • 原文地址:https://www.cnblogs.com/xiaochen1024/p/14481462.html
Copyright © 2020-2023  润新知