您现在的位置是:首页 > 文章详情

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

日期:2021-06-24点击:323
## react源码解析19.手写迷你版react #### 视频课程(高效学习):[进入课程](https://xiaochen1024.com/series/60b1b600712e370039088e24/60b1b636712e370039088e25) #### 课程目录: [1.开篇介绍和面试题](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b311cf10a4003b634719) [2.react的设计理念](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b31ccf10a4003b63471a) [3.react源码架构](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b328cf10a4003b63471b) [4.源码目录结构和调试](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b32ecf10a4003b63471c) [5.jsx&核心api](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b334cf10a4003b63471d) [6.legacy和concurrent模式入口函数](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b33acf10a4003b63471e) [7.Fiber架构](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b340cf10a4003b63471f) [8.render阶段](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b348cf10a4003b634720) [9.diff算法](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b354cf10a4003b634721) [10.commit阶段](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b360cf10a4003b634722) [11.生命周期](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b367cf10a4003b634723) [12.状态更新流程](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b36ecf10a4003b634724) [13.hooks源码](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b374cf10a4003b634725) [14.手写hooks](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b37acf10a4003b634726) [15.scheduler&Lane](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b556cf10a4003b634727) [16.concurrent模式](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b55ccf10a4003b634728) [17.context](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b564cf10a4003b634729) [18事件系统](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b56ccf10a4003b63472a) [19.手写迷你版react](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b57bcf10a4003b63472b) [20.总结&第一章的面试题解答](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b581cf10a4003b63472c) [21.demo](https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b587cf10a4003b63472d) #### 迷你react和真正的源码有哪些区别呢 - 在render阶段我们遍历了整颗Fiber树,在源码中如果节点什么都没改变会命中优化的逻辑,然后跳过这个节点的遍历 - commit我们也遍历了整颗Fiber树,源码中只遍历带有effect的Fiber节点,也就是遍历effectList - 每次遍历的时候我们都是新建节点,源码中某些条件会复用节点 - 没有用到优先级 第一步:渲染器和入口函数 ```js const React = { createElement, render, }; const container = document.getElementById("root"); const updateValue = (e) => { rerender(e.target.value); }; const rerender = (value) => { const element = (

Hello {value}

); React.render(element, container); }; rerender("World"); ``` 第二步:创建dom节点函数 ```js /创建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; } ``` 第三步:更新节点函数 ```js 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阶段 ```js //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阶段 ```js //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); } ``` 第六步:开始调度 ```js //渲染开始的入口 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); ``` 完整代码 ```js //创建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 = (

Hello {value}

); React.render(element, container); }; rerender("World"); ```
原文链接:https://blog.51cto.com/u_11890769/2942846
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章