首页  >  文章  >  web前端  >  构建一个小型 React ChSX

构建一个小型 React ChSX

Mary-Kate Olsen
Mary-Kate Olsen原创
2024-10-20 18:29:30304浏览

Build a Tiny React ChSX

本系列将构建一个小型前端框架,其功能类似于 React,以说明 React 在幕后的工作原理。本章涵盖 JSX。

我将使用 Bun 作为运行时。 Node 可能需要额外的配置来进行打字稿和编译。

本教程基于本教程,但使用了 JSX、Typescript 和更简单的实现方法。您可以在我的 GitHub 存储库上查看注释和代码。

JSX

现在,在我们进一步深入之前,让我们先看看 React-jsx 的几个重要元素。

如果你看过 React 组件的转译代码,你会发现它只是一堆函数调用。 JSX 只是 React.createElement 的语法糖。也就是说,例如,以下 JSX 代码:

const element = <h1 className="greeting">Hello, world!</h1>;

将被转译为:

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

React.createElement 会创建一个虚拟元素,这是另一个核心机制。简单来说,虚拟元素就是虚拟DOM中的元素。虚拟 DOM 是代表实际 DOM 的东西。由于操作虚拟DOM只是操作js对象,所以比操作实际DOM要快很多。我们将在下一章讨论虚拟 DOM。但就目前而言,知道 JSX 只是 React.createElement 的语法糖就足够了。

React.createElement 函数按顺序接受以下参数,

  1. 元素的标签名称。有些标签是特殊的,如 div、span、h1 等。这些称为内在元素。有些标签是用户定义的组件。
  2. 元素的 props。这是一个包含元素属性的对象。例如,上面 h1 元素的 className 是greeting。
  3. 元素的子元素。这可以是字符串或元素。请注意,该参数在函数签名中表示为 ...children,这意味着它可以采用任意数量的参数。

这听起来很容易,对吧?那么我们就开始吧。

实施 JSX

编译时,我们可以指定要使用的函数——默认函数是React.createElement。但我们可以使用我们自己的函数。

所以我们创建一个v-dom.ts,以便先定义虚拟元素。

export type VDomAttributes = { 
    key?: string | number
    [_: string]: string | number | boolean | Function | undefined
}

export interface VDomElement {
  kind: 'element'
  tag: string
  children?: VDomNode[]
  props?: VDomAttributes
  key: string | number | undefined
}

export type VDomNode = 
| string
| VDomElement

请注意,我们在每个节点中都有一个关键字段(节点只是文本或元素的名称)。这是为了和解,我们将在下一章中讨论。您现在可以安全地忽略它。

现在我们可以实现createElement函数了。我们把它放在同一个文件中。

export function createElement(tag: string, props: VDomAttributes, ...children: VDomNode[]): VDomElement {
  console.log('createElement', tag, props, children)
    return {
        kind: 'element',
        tag,
        children,
        props,
        key: props?.key ?? undefined
    }
}

现在我们指示编译器使用这个函数。我们可以通过将以下行添加到文件顶部来做到这一点。

const element = <h1 className="greeting">Hello, world!</h1>;

请注意,由于我们采用React标准,因此我们需要引入React类型定义。我们可以通过将以下行添加到文件顶部来做到这一点。

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

然后在 tsconfig.json 中,我们将以下行添加到 compilerOptions 字段。

export type VDomAttributes = { 
    key?: string | number
    [_: string]: string | number | boolean | Function | undefined
}

export interface VDomElement {
  kind: 'element'
  tag: string
  children?: VDomNode[]
  props?: VDomAttributes
  key: string | number | undefined
}

export type VDomNode = 
| string
| VDomElement

现在,我们可以看看我们创建的虚拟元素。

export function createElement(tag: string, props: VDomAttributes, ...children: VDomNode[]): VDomElement {
  console.log('createElement', tag, props, children)
    return {
        kind: 'element',
        tag,
        children,
        props,
        key: props?.key ?? undefined
    }
}

您将看到我们基于 JSX 代码定义的虚拟 dom 元素。

此外,如果您不知道它的正式名称,我们还可以定义一个片段元素 -

在处理fragment时,编译器会将配置好的fragment工厂带到元素创建函数中的标签中。这与函数式组件的工作方式相同——函数式组件会将函数传递给标签,我们将在下一章中演示这一点。

尽管如此,在我们的实现中,不需要进行复杂的处理 - 我们只需要为片段设置一个特殊的标签即可。

import { createElement as h } from './v-dom'

额外的编译器选项,

bun i @types/react

基本上,fragment 只是一个带有空标签的特殊元素。当创建fragment时,编译器会获取jsxFragmentFactory并将其放入createElement第一个参数的tag参数中。所以我们可以很容易地将片段与其他元素区分开来。

"compilerOptions": {
    "jsx": "react",
    "jsxFactory": "createElement",
}

此代码将正确生成虚拟 DOM。到目前为止,我们已经实现了小型 React 的 JSX 部分。

脚注

呃,这是第三章的作者。事实上,目前 JSX 的实现并不完美。我们将在第三章修复它。现在它不支持诸如
之类的语法

import { createElement } from "./v-dom";

function App() {
    return <div>
        <h1>a</h1>
        <h2>b</h2>
    </div>
}

console.log(App());

这是因为每个 {} 都被视为一个子项,而映射则返回一个数组。所以它会有嵌套的孩子。

此外,我们还不支持功能组件,这将在下一章中介绍。

您可以按照当前的方式进行操作,稍后再修复。抱歉给您带来不便。

以上是构建一个小型 React ChSX的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn