# mini-react **Repository Path**: liuzhe163/mini-react ## Basic Information - **Project Name**: mini-react - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-01-16 - **Last Updated**: 2024-01-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # mini-react 崔学社 # mini-react 笔记 ## 第一节:实现最简单 mini-react 目标是:页面上能显示 app, 代码如下: ```js const dom = document.createElement("div"); dom.id = "app"; document.querySelector("#root").append(dom); const textNode = document.createTextNode(""); textNode.nodeValue = "app"; dom.append(textNode); ``` 1. 使用对象描述 el ```js const el = { type: "div", props: { id: "app", children: [ { type: "TEXT_ELEMENT", props: { nodeValue: "app", children: [], }, }, ], }, }; ``` 2. 使用抽离 textElement ```js const textEl = { type: "TEXT_ELEMENT", props: { nodeValue: "app", children: [], }, }; const el = { type: "div", props: { id: "app", children: [textEl], }, }; ``` 3. 使用对象中的属性代替直接赋值 ```js const app = document.createElement(el.type); app.id = el.props.id; document.querySelector("#root").append(app); const textNode = document.createTextNode(""); textNode.nodeValue = textEl.props.nodeValue; app.append(textNode); ``` 4. 动态创建 ```js function createTextNode(text) { return { type: "TEXT_ELEMENT", props: { nodeValue: text, children: [], }, }; } function createElement(type, props, ...children) { return { type, props: { ...props, children, }, }; } const textEl = createTextNode("app"); const el = createElement("div", { id: "app" }, textEl); ``` 依旧正常显示 app 5. 创建 render 函数来统一处理 ```js function render(el, container) { // 根据不同类型创建不同的dom const dom = el.type === "TEXT_ELEMENT" ? document.createTextNode("") : document.createElement(el.type); // 添加属性 Object.keys(el.props).forEach((key) => { if (key !== "children") { dom[key] = el.props[key]; } }); // 处理子元素 const children = el.props.children; children.forEach((child) => { render(child, dom); }); // 添加到容器中 container.append(dom); } // 使用render const App = createElement("div", { id: "app" }, "app", "---hi mini-react"); console.log(App); render(App, document.querySelector("#root")); ``` 6. 根据 react 的 api 构建核心方法 ```js const ReactDOM = { createRoot(container) { return { render(el) { render(el, container); }, }; }, }; ReactDOM.createRoot(document.querySelector("#root")).render(App); ``` ### 学到 - 数据结构设计的合理可以降低解构的算法复杂度。 ### 引入 jsx 使用 vite 就能直接使用 jsx ## 第二节 实现任务调度器 ### 使用 window.requestIdleCallback ```js // 可以发现每个任务的剩余时间都是不一样的,这就是requestIdleCallback的特点,它会根据当前浏览器的负载情况,来分配时间片,这样就不会影响到用户的交互体验。 function worker(IdleDeadline) { // Description: demo for requestIdleCallback console.log(IdleDeadline.timeRemaining()); } window.requestIdleCallback(worker); ``` ### 改造 react.js 成任务处理 ```js let taskId = 0; function worker(deadline) { taskId++; let shouldYield = false; while (!shouldYield) { // task run 任务运行 console.log(`task id ${taskId}执行`); // dom 渲染 shouldYield = deadline.timeRemaining() < 1; } requestIdleCallback(worker); } requestIdleCallback(worker); ``` ### 把渲染的树转化成可以让任务一个接着一个执行的链表 **转化规则是:** 1. child 2. sibling 3. 叔叔 **initChildren 函数分析:** ```js function initChildren(fiber) { const children = fiber.props.children; let prevChild = null; children.forEach((child, index) => { const newFiber = { type: child.type, props: child.props, child: null, parent: fiber, sibling: null, dom: null, }; if (index === 0) { fiber.child = newFiber; } else { // 每次给当前节点的上一个设置自身为sibling prevChild.sibling = newFiber; } // 每次都把上一个节点赋值给prevChild prevChild = newFiber; }); } ``` 主要工作是遍历 fiber 的 children,把 fiber 的 child 信息完善成包含 parent、sibling、dom 信息的 newFiber。由于 每次执行会记录当前 fiber 为下一个任务的 prevChild,所以在不是第一个索引的时候设置上一个 prevChild 的 sibling 值是当前 newFiber。 ### 在线演示树表转链表演示 [链接](https://pythontutor.com/render.html#mode=edit) ## 第三节实现统一提交和支持 function component > function component 以下简称 FC **上一节代码遗留的问题** 如果执行过程中遇到 requestIdleCallback 没有空余时间,且间隔时间较长(1-2s)后才有空余时间,用户看到的可能是只渲染了几个节点,然后之后才渲染剩余节点。能不能做到统一添加到父节点的。 ### 小步走实现思路: 1. type 的处理 2. 区分 FC 和非 FC 3. 添加到视图的处理 **先试试直接使用**: ```js function Count() { return