• react源码解析19.手写迷你版react


    react源码解析19.手写迷你版react

    视频课程(高效学习):进入课程

    课程目录:

    1.开篇介绍和面试题

    2.react的设计理念

    3.react源码架构

    4.源码目录结构和调试

    5.jsx&核心api

    6.legacy和concurrent模式入口函数

    7.Fiber架构

    8.render阶段

    9.diff算法

    10.commit阶段

    11.生命周期

    12.状态更新流程

    13.hooks源码

    14.手写hooks

    15.scheduler&Lane

    16.concurrent模式

    17.context

    18事件系统

    19.手写迷你版react

    20.总结&第一章的面试题解答

    21.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节点函数

    /创建element
    function createElement(type, props, ...children) {
      return {
        type,
        props: {
          ...props,
          children: children.map((child) => (typeof child === "object" ? child : createTextElement(child))),
        },
      };
    }
    
    //创建text类型
    function createTextElement(text) {
      return {
        type: "TEXT_ELEMENT",
        props: {
          nodeValue: text,
          children: [],
        },
      };
    }
    
    //创建dom
    function createDom(fiber) {
      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阶段

    //render阶段
    function performUnitOfWork(fiber) {
      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) {
      let index = 0;
      let oldFiber = wipFiber.alternate && wipFiber.alternate.child;
      let prevSibling = null;
      while (index < elements.length || (oldFiber !== null && oldFiber !== undefined)) {
        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阶段

    //commit阶段
    function commitRoot() {
      deletions.forEach(commitWork);
      commitWork(wipRoot.child);
      currentRoot = wipRoot;
      wipRoot = null;
    }
    
    //操作真实dom
    function commitWork(fiber) {
      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;
    }
    
    //调度函数
    function workLoop(deadline) {
      let shouldYield = false;
      while (nextUnitOfWork && !shouldYield) {
        //render阶段
        nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
        shouldYield = deadline.timeRemaining() < 1;
      }
    
      if (!nextUnitOfWork && wipRoot) {
        commitRoot(); //commit阶段
      }
    
      requestIdleCallback(workLoop); //空闲调度
    }
    
    requestIdleCallback(workLoop);
    

    完整代码

    //创建element
    function createElement(type, props, ...children) {
      return {
        type,
        props: {
          ...props,
          children: children.map((child) => (typeof child === "object" ? child : createTextElement(child))),
        },
      };
    }
    
    //创建text类型
    function createTextElement(text) {
      return {
        type: "TEXT_ELEMENT",
        props: {
          nodeValue: text,
          children: [],
        },
      };
    }
    
    //创建dom
    function createDom(fiber) {
      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]);
        });
    }
    
    //commit阶段
    function commitRoot() {
      deletions.forEach(commitWork);
      commitWork(wipRoot.child);
      currentRoot = wipRoot;
      wipRoot = null;
    }
    
    //操作真实dom
    function commitWork(fiber) {
      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);
    }
    
    let nextUnitOfWork = null;
    let currentRoot = null;
    let wipRoot = null;
    let deletions = null;
    
    //渲染开始的入口
    function render(element, container) {
      wipRoot = {
        dom: container,
        props: {
          children: [element],
        },
        alternate: currentRoot,
      };
      deletions = [];
      nextUnitOfWork = wipRoot;
    }
    
    //调度函数
    function workLoop(deadline) {
      let shouldYield = false;
      while (nextUnitOfWork && !shouldYield) {
        //render阶段
        nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
        shouldYield = deadline.timeRemaining() < 1;
      }
    
      if (!nextUnitOfWork && wipRoot) {
        commitRoot(); //commit阶段
      }
    
      requestIdleCallback(workLoop); //空闲调度
    }
    
    requestIdleCallback(workLoop);
    
    //render阶段
    function performUnitOfWork(fiber) {
      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) {
      let index = 0;
      let oldFiber = wipFiber.alternate && wipFiber.alternate.child;
      let prevSibling = null;
      while (index < elements.length || (oldFiber !== null && oldFiber !== undefined)) {
        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");
    
    
  • 相关阅读:
    [转] Immutable 常用API简介
    [转] 组件库按需加载 借助babel-plugin-import实现
    [转] react-router4 + webpack Code Splitting
    [转] React Hot Loader 3 beta 升级指南
    [转] 如何写好.babelrc?Babel的presets和plugins配置解析
    [转] webpack热更新配置小结
    [转] 学会fetch的用法
    [转] webpack3.0踩坑:postcss-loader的使用
    [转] 详解webpack-dev-server的使用
    webpack 使用环境变量
  • 原文地址:https://www.cnblogs.com/xiaochen1024/p/14925230.html
Copyright © 2020-2023  润新知