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;
相关推荐
喜欢踢足球的老罗27 分钟前
RN开发搬砖经验之—React Native(RN)应用转原生化-Android 平台
android·react native·react.js
yqcoder1 小时前
react 中 useCallback Hook 作用
前端·javascript·react.js
jun7788951 小时前
React状态管理之Redux
前端·react.js·前端框架
熊的猫1 小时前
实现 Toy-React , 实现 JSX 渲染
前端·javascript·chrome·react.js·webpack·前端框架·node.js
qq_544329171 小时前
关于写React的一些反思和总结
前端·react.js·前端框架
前端熊猫8 小时前
React 项目与 Vue 项目的区别
javascript·vue.js·react.js
前端郭德纲16 小时前
浅谈React的虚拟DOM
前端·javascript·react.js
yqcoder18 小时前
react 中 memo 模块作用
前端·javascript·react.js
优雅永不过时·19 小时前
Three.js 原生 实现 react-three-fiber drei 的 磨砂反射的效果
前端·javascript·react.js·webgl·threejs·three