react源码解析19.手写迷你版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节点函数

//创建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");
作者:长腿程序员原文地址:https://segmentfault.com/a/1190000043274495

%s 个评论

要回复文章请先登录注册