创建自己的 React (上)

前言

使用 vite, typescript 从零开始创建自己的 React,命名为 hypereact。

起步

通过 vite 创建一个项目,不选择任何框架

sh 复制代码
pnpm create vite

创建 vite.config.js 文件,添加 esbuild.loader 选项,设置为 tsx

js 复制代码
export default defineConfig({
  esbuild: {
    loader: 'tsx',
  },
});

createElement 函数

新建 hypereact.ts,在这个文件实现功能代码。

首先需要一个创建 createElement 函数,在 JSX 转换成 JS 的时候调用它,生成一个具有 type 和 props 和 children 的对象,使用这个对象就能生成真实的 DOM 节点。

这个对象的 type 指的是DOM 节点的类型,props 是节点的属性,children 是一个数组,可以包含多种类型的数据,如果是数字或者字符串这种原始类型的值,就将它设置为 TEXT_ELEMENT 类型。

ts 复制代码
/// hypereact.ts

const TEXT_ELEMENT = 'TEXT_ELEMENT';

export function createElement(type: string, props: any, ...children: any[]) {
  return {
    type,
    ...props,
    children: children.map((child) =>
      typeof child === 'object' ? child : createTextElement(child),
    ),
  };
}

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

render 函数

render 函数的作用是将元素渲染到页面上,它接收 element 和 container 两个参数。

首先需要判断 element 的类型,如果是原始类型,就调用 document.createTextNode 创建文本节点,否则根据 element.type 创建对应的 DOM 节点。

然后将 props 属性添加到 DOM 节点上,对 element 的 children 递归调用 render 函数,从而生成完整的节点树。

最后将生成的 DOM 节点添加到 container 里面。

ts 复制代码
export function render(element, container) {
  const dom: HTMLElement =
    element.type == TEXT_ELEMENT
      ? document.createTextNode('')
      : document.createElement(element.type);
  const isProperty = (key) => key !== 'children';

  Object.keys(element.props)
    .filter(isProperty)
    .forEach((name) => {
      if (dom.setAttribute) {
        dom.setAttribute(name, element.props[name]);
      } else {
        dom[name] = element.props[name];
      }
    });

  element.props.children.forEach((child) => render(child, dom));

  container.appendChild(dom);
}

使用

导出 Hypereact 对象

ts 复制代码
/// hypereact.ts
/// ...

const Hypereact = {
  createElement,
  render,
};

export default Hypereact;

添加 App.tsx 文件,使用 /** @jsx Hypereact.createElement */ 注释,告诉构建工具在翻译 JSX 时使用这个函数。

tsx 复制代码
/// App.tsx
/// import Hypereact from './hypereact';
/// ...

/** @jsx Hypereact.createElement */
const App = (
  <div>
    <a href="https://vitejs.dev" target="_blank">
      <img src={viteLogo} class="logo" alt="Vite logo" />
    </a>
    <a
      href="https://developer.mozilla.org/en-US/docs/Web/JavaScript"
      target="_blank"
    >
      <img src={javascriptLogo} class="logo vanilla" alt="JavaScript logo" />
    </a>
    <h1>Hello Hypereact!</h1>
    <div class="card">
      <button id="counter" type="button">
        {`count is ${counter}`}
      </button>
    </div>
    <p class="read-the-docs">Click on the Vite logo to learn more</p>
  </div>
);

export default App;

在 main.js 中使用 render 函数,App.jsx 的内容被渲染到页面上了。

js 复制代码
import './style.css';
import App from './App';
import Hypereact from './hypereact';

const container = document.getElementById('app');
Hypereact.render(App, container);

参考

源码

Build your own React

本文完,感谢阅读 🌹

相关推荐
kyriewen1 小时前
Skills 榜前 10 我全装了:只有 3 个能直接用
前端·javascript·程序员
IT_陈寒1 小时前
当Java的hashCode遇上Lombok,我的缓存被全清了
前端·人工智能·后端
工控丫丫2 小时前
帆软看板中经常使用的公式函数及经常搭的组合
前端·javascript·数据库
jerrywus2 小时前
我用 GPT-6.1-sol 做了台会讲解的 3D 售货机:60 罐饮料,一口没喝上
前端·agent·claude
孟陬3 小时前
Foobar 技术周刊#159:当看不懂代码时,如何高效排障调试。脏页陷阱:一条只读查询,写入数 TB 数据。推测解码:大模型加速利器
前端·人工智能·后端
Zhao-Jichao3 小时前
AI Prompting for Everyone 课程笔记 - P17 图像生成 Imaging Generation
前端
Highcharts.js4 小时前
智能数据分析组件Orbit 正式发布与快速上手体验教程
前端·人工智能·信息可视化·数据分析系统·highcharts·orbit·智能可视化
螺丝钉tina5 小时前
[Playwright ][16]Playwright + TypeScript API 自动化测试实战:API测试
前端·javascript·typescript
小天源5 小时前
智叟云集系统 · 架构与职责分层
服务器·前端·数据库
Highcharts.js7 小时前
React 中原生支持使用 JSX 组件配置自定义表格 Highcharts Grid
前端·javascript·react.js·前端框架·jsx·highcharts·自定义表格