本系列将构建一个小型前端框架,其功能类似于 React,以说明 React 在幕后的工作原理。本章涵盖 JSX。
我将使用 Bun 作为运行时。 Node 可能需要额外的配置来进行打字稿和编译。
本教程基于本教程,但使用了 JSX、Typescript 和更简单的实现方法。您可以在我的 GitHub 存储库上查看注释和代码。
现在,在我们进一步深入之前,让我们先看看 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 函数按顺序接受以下参数,
这听起来很容易,对吧?那么我们就开始吧。
编译时,我们可以指定要使用的函数——默认函数是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中文网其他相关文章!