七天快速学完mini-react ,再也不担心不会原理了(第一天)

当你想要快速掌握React开发技能却又感到困惑时,七天学会mini-react将成为你的最佳选择!这款全新的学习工具不仅简洁易懂,还能帮助你在短短七天内掌握React的精髓。无需繁琐的教程,无需枯燥的学习过程,只需七天,你就能成为React开发的高手!赶快加入我们,一起探索无限可能吧!#学习React #快速掌握技能 #七天挑战 #mini-react

1、七天搞定mini-react

第一天: 实现最简 mini-react

实现最简 mini-react

首先我们试着实现一下render函数,在这之前我们可以先看看是它是如何渲染到页面上的,如果要是我们会怎么做?

我们先来创建两个文件

html 复制代码
// index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="root"></div>
</body>
<script src="./main.js" type="module"></script>

</html>
js 复制代码
// main.js
const dom = document.createElement("div")
dom.id = "app"
document.querySelector("#root").append(dom)
const textNode = document.createTextNode("")
textNode.nodeValue = "app"
dom.appendChild(textNode)

我们创建了一个idrootdiv,我们需要在root里面创建一个idappdiv,我们通过原生方法去创建,并且我们还创建了一个textNode节点,并赋值为app,最后把文本节点放在了新创建的div中,这样就简单的完成了app的挂载

接下来我们对代码进行优化,我们使用对象来模拟节点,相当于虚拟DOM的方式,文本类型就是TEXT_ELEMENT

js 复制代码
const textEl = {
  type: "TEXT_ELEMENT",
  props: {
    nodeValue: "app",
    children: [],
  },
}
const el = {
  type: "div",
  props: {
    id: "app",
    children: [textEl],
  },
}
const dom = document.createElement(el.type)
dom.id = el.props.id
document.querySelector("#root").append(dom)
const textNode = document.createTextNode("")
textNode.nodeValue = textEl.props.nodeValue
dom.appendChild(textNod

接下来我们继续对代码进行优化,发现很多代码都是在创建元素,接下来我们来实现具体方法,我们创建两个方法,专门用来创建普通元素,以及文本元素

js 复制代码
function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map(child => {
        return typeof child === "string" ? createTextNode(child) : child
      }),
    },
  }
}

function createTextNode(text, ...children) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children,
    },
  }
}

const textEl = createTextNode("app")
const App = createElement("div", { id: "app" }, textEl)

const dom = document.createElement(App.type)
dom.id = App.props.id
document.querySelector("#root").append(dom)

const textNode = document.createTextNode("")
textNode.nodeValue = textEl.props.nodeValue
dom.appendChild(textNode)

通过运行,发现在处理普通元素的时候,需要对children进行处理,如果内容为文本元素,需要使用createTextNode方法,修改完之后,页面正确显示app

接下来我们就可以来实现render方法啦!

这里在处理el.propsel.children时,需要分开处理,使用递归的方式,就可以实现render

js 复制代码
function render(el, container) {
  const dom = el.type === "TEXT_ELEMENT" ? document.createTextNode("") : document.createElement(el.type)

  // 设置id和class
  Object.keys(el.props).forEach(key => {
    if (key !== "children") {
      // 给DOM创建props
      dom[key] = el.props[key]
    }
  })

  const children = el.props.children
  children.forEach(child => {
    render(child, dom)
  })
  container.append(dom)
}

const textEl = createTextNode("app")
// const App = createElement("div", { id: "app" }, textEl)
const App = createElement("div", { id: "app" }, "hi-", "mini-react")
render(App, document.querySelector("#root"))

我们可以修改children的内容,发现运行十分成功,非常完美!

我们打印下app的内容,发现跟我们的虚拟dom一模一样

接下来我们来实现这个吧

js 复制代码
const ReactDOM = {
  createRoot(container) {
    return {
      render(el) {
        render(el, container)
      },
    }
  },
}
ReactDOM.createRoot(document.querySelector("#root")).render(App)

通过运行,我们发现没有问题,这样我们就已经实现了

接下来我们对代码进行抽离

将代码进行抽离

js 复制代码
// React.js
function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      children: children.map(child => {
        return typeof child === "string" ? createTextNode(child) : child
      }),
    },
  }
}

function createTextNode(text, ...children) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children,
    },
  }
}

function render(el, container) {
  const dom = el.type === "TEXT_ELEMENT" ? document.createTextNode("") : document.createElement(el.type)

  // 设置id和class
  Object.keys(el.props).forEach(key => {
    if (key !== "children") {
      // 给DOM创建props
      dom[key] = el.props[key]
    }
  })

  const children = el.props.children
  children.forEach(child => {
    render(child, dom)
  })
  container.append(dom)
}

const React = {
  render,
  createElement,
}

export default React
js 复制代码
// ReactDom.js
import React from "./React.js"

const ReactDOM = {
  createRoot(container) {
    return {
      render(el) {
        React.render(el, container)
      },
    }
  },
}

export default ReactDOM
js 复制代码
// App.js
import React from "./core/React.js"

const App = React.createElement("div", { id: "app" }, "hi-", "mini-react")

export default App
js 复制代码
// index.js
import ReactDOM from "./core/ReactDom.js"
import App from "./App.js"

ReactDOM.createRoot(document.querySelector("#root")).render(App)

这样我们就已经完成了简单的mini-react,但是,我们毕竟是用js来实现的,但是一般我们是使用jsx啊,那怎么办呢?后面我们就来实现jsx的版本,请等待下一章更新

使用 jsx

扩展 - 使用 vitest 做单元测试

扩展 - 自定义 react 的名字

相关推荐
zqx_713 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
TonyH20021 天前
webpack 4 的 30 个步骤构建 react 开发环境
前端·css·react.js·webpack·postcss·打包
掘金泥石流1 天前
React v19 的 React Complier 是如何优化 React 组件的,看 AI 是如何回答的
javascript·人工智能·react.js
lucifer3111 天前
深入解析 React 组件封装 —— 从业务需求到性能优化
前端·react.js
秃头女孩y2 天前
React基础-快速梳理
前端·react.js·前端框架
sophie旭2 天前
我要拿捏 react 系列二: React 架构设计
javascript·react.js·前端框架
BHDDGT2 天前
react-问卷星项目(5)
前端·javascript·react.js
liangshanbo12152 天前
将 Intersection Observer 与自定义 React Hook 结合使用
前端·react.js·前端框架
黄毛火烧雪下2 天前
React返回上一个页面,会重新挂载吗
前端·javascript·react.js