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;
相关推荐
TE-茶叶蛋1 小时前
Flutter、Vue 3 和 React 在 UI 布局比较
vue.js·flutter·react.js
西瓜_号码5 小时前
React中Redux基础和路由介绍
javascript·react.js·ecmascript
伟笑6 小时前
React 的常用钩子函数在Vue中是如何设计体现出来的。
前端·react.js
FogLetter7 小时前
React组件开发进阶:本地存储与自定义Hooks的艺术
前端·javascript·react.js
AliciaIr9 小时前
深入React事件机制:解密“合成事件”与“事件委托”的底层奥秘
javascript·react.js
心.c11 小时前
后台管理系统-权限管理
javascript·react.js·github
杨进军11 小时前
实现 React 函数组件渲染
前端·react.js·前端框架
归于尽11 小时前
被 50px 到 200px 的闪烁整破防了?useLayoutEffect 和 useEffect 的区别原来在这
前端·react.js
杨进军11 小时前
实现 React Fragment 节点渲染
前端·react.js·前端框架
杨进军11 小时前
实现 React 类组件渲染
前端·react.js·前端框架