评论

收藏

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

开发技术 开发技术 发布于:2021-06-24 10:26 | 阅读数:561 | 评论:0

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 = (
  
    <input onInput={updateValue} value={value} />
    <h2>Hello {value}</h2><br>
  
  );
  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 = (
  
    <input onInput={updateValue} value={value} />
    <h2>Hello {value}</h2><br>
  
  );
  React.render(element, container);
};
rerender("World");
关注下面的标签,发现更多相似文章