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;
相关推荐
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ1 天前
React 怎么区分导入的是组件还是函数,或者是对象
前端·react.js·前端框架
前端无涯1 天前
React/Vue 代理配置全攻略:Vite 与 Webpack 实战指南
vue.js·react.js
San301 天前
深度解析 React 组件化开发:从 Props 通信到样式管理的进阶指南
前端·javascript·react.js
AAA阿giao1 天前
深度解析 React 项目架构:从文件结构到核心 API 的全面拆解
前端·javascript·react.js
昨晚我输给了一辆AE861 天前
关于 react-hook-form 的 isValid 在有些场景下的值总是 false 问题
前端·react.js
Mintopia1 天前
🏗️ B端架构中的用户归因与埋点最佳实践
前端·react.js·架构
Blossom.1182 天前
大模型AI Agent实战:ReAct框架从零实现与金融研报分析系统
人工智能·学习·react.js·stable diffusion·金融·aigc·知识图谱
南山安2 天前
React 学习:父传子的单项数据流——props
javascript·react.js·前端框架
soda_yo2 天前
React哲学:保持组件纯粹 哈气就要哈得纯粹
前端·react.js·设计