mini-react(一)实现最简mini-react

本节目标:在页面中呈现 app(使用React的API)

仓库链接:github.com/zhuhaohe-co...

效果如下:

js 复制代码
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
ReactDoM.createRoot(document.getElementById( "root")).render(<App >);

实现思路大致为以下五步:

  1. 动态创建虚拟dom (注意虚拟dom的形式,要符合jsx的规范)
  2. 创建渲染函数
  3. 重构API
  4. 使用jsx代替掉js写法
  5. 借助vite实现jsx的解析

创建虚拟dom

虚拟dom是一个对象,是对真实dom的描述,形式如下:

js 复制代码
//div节点
const divElement = {
    type:'div',
    props:{
        id:'app',
        children:[]
    }
}

对于文本节点,我们需要用到document.createTextNode这个API去创建,所以要和其它节点做一个区分,故我们将其type固定,便于区分

js 复制代码
const textElement = {
    type:'TEXT_ELEMENT',
    props:{
        nodeValue: 'hello world'
        children:[]
    }
}

知道了虚拟dom的形式,我们就可以开始编写函数去动态地创建虚拟dom了:

js 复制代码
function createElement(type, props, ...children) {
  return {
    type,
    props: {
      ...props,
      //如果子节点是文本, 调用创建节点的函数处理一下
      children: children.map((child) =>
        typeof child === "string" ? createTextElement(child) : child
      ),
    },
  };
}

function createTextElement(text) {
  return {
    type: "TEXT_ELEMENT",
    props: {
      nodeValue: text,
      children: [],
    },
  };
}

实现render函数

有了虚拟dom,我们就可以将其转换为真实dom,并插入到父容器中,我们通过render函数来实现这一点,可以分为三部分:

  1. 虚拟dom--->真实dom
  2. 添加props
  3. 递归处理children
js 复制代码
function render(container, el) {
  //虚拟dom--->真实dom
  const dom =
    el.type === "TEXT_ELEMENT"
      ? document.createTextNode(el.nodeValue)
      : document.createElement(el.type);
  //添加props
  Object.keys(el.props).forEach((key) => {
    if (key !== "children") {
      dom[key] = el.props[key];
    }
  });
  //递归处理children
  const children = el.props.children;
  children.forEach((child) => {
    render(dom, child);
  });
  container.append(dom);
}

重构API

核心代码已实现,我们可以将其封装一下,重构为React提供的API的形式:

js 复制代码
//React.js (函数具体实现见上文)
export default {
  createElement,
  render,
};
js 复制代码
//ReactDOM.js
import React from "./React.js";
const ReactDOM = {
  createRoot(container) {
    return {
      render(el) {
        React.render(container, el);
      },
    };
  },
};

export default ReactDOM;

接下来我们在main.js中引入测试一下:

js 复制代码
import ReactDOM from "./core/ReactDOM.js";
import React from "./core/React.js";

const App = React.createElement("div", { id: "app" }, "hello", "-", "world");
ReactDOM.createRoot(document.querySelector("#root")).render(App);

效果如下,符合我们的预期:

现在我们需要解决的是对 jsx 的处理,解析jsx的工具有很多,如 Webpack、Vite、Babel 等。这里我们选择 Vite 来解析 jsx。

借助 Vite 实现 JSX 的解析

Vite 解析 jsx 的时候,会调用React.createElement函数。这也是为什么我们平时在项目中并没有使用React却需要引入的原因。

我们可以打印一下看看效果:

jsx 复制代码
function Test() {
  return <div id="test">test</div>;
}
console.log(Test);

输出如下:

我们将App改写为jsx的形式即可,注意要正确引入编写的 React.js 和 ReactDOM.js

jsx 复制代码
import React from "./core/React.js";
const App = <div id="app">hello world</div>;

export default App;
相关推荐
牛仔只喝牛奶3 小时前
多端跨端技术选型:小程序 + H5 + App 的两栈拆分落地
react native·react.js
wear工程师6 小时前
setState 到底是不是异步的?这题从 React 16 问到 18,标准答案早就变了
react.js·面试
wanghowie8 小时前
LangGraph4j 落地(二)— ReAct 多步工具与 trace 扩展
前端·react.js·前端框架
csj509 小时前
前端基础之《React(11)—state声明式》
前端·react.js
张元清10 小时前
React useLocalStorage Hook:SSR 安全的持久化状态(2026)
javascript·react.js
析数塔10 小时前
2026前端框架深度解析:React Compiler、Angular Zoneless、Vue 3.5重写游戏规则
前端·vue.js·react.js
杖雍皓10 小时前
没有服务器如何构建现代 Web 应用:平台选型与组合策略
运维·服务器·前端·react.js·github·netlify·vercel
sun_weitao10 小时前
微前端框架之qiankun 怎么用?vue3+vite+qiankun 和 react + qiankun
前端·react.js·前端框架
名字还没想好☜1 天前
React useEffect 依赖数组踩坑:闭包陷阱与清理函数
前端·javascript·react.js·react·useeffect
我是大卫1 天前
【图】React源码解析-第四部分:事件与跨层传递
react.js·源码