创建自己的 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 的时候调用它,生成一个具有 typepropschildren 的对象,使用这个对象就能生成真实的 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 函数的作用是将元素渲染到页面上,它接收 elementcontainer 两个参数。

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

然后将 props 属性添加到 DOM 节点上,对 elementchildren 递归调用 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

本文完,感谢阅读 🌹

相关推荐
大厂码农老A20 分钟前
P10老板一句‘搞不定就P0’,15分钟我用Arthas捞回1000万资损
java·前端·后端
拜无忧29 分钟前
【教程】Vue 3 项目架构终极指南:一份面向新手的、高性能的实战教程
前端·vue.js
星海穿梭者39 分钟前
SQL SERVER 查看锁表
java·服务器·前端
一枚前端小能手41 分钟前
「周更第5期」实用JS库推荐:RxJS
前端·javascript·rxjs
影i41 分钟前
关于浏览器 Cookie 共享机制的学习与梳理
前端
文心快码BaiduComate43 分钟前
文心快码已接入GLM-4.6模型
前端·后端·设计模式
RoyLin1 小时前
C++ 原生扩展、node-gyp 与 CMake.js
前端·后端·node.js
我是天龙_绍1 小时前
二进制散列值 搞 权限组合,记口诀:| 有1则1 ,&同1则1
前端
江城开朗的豌豆1 小时前
拆解微信小程序的“积木盒子”:这些原生组件你都玩明白了吗?
前端·javascript·微信小程序
爱吃甜品的糯米团子1 小时前
CSS Grid 网格布局完整指南:从容器到项目,实战详解
前端·css