文档 / rescript-react / 超越 JSX
Edit

超越 JSX

JSX 是一个语法糖,允许我们以类似 HTML 的方式组合 React 组件。要使用 JSX,组件需要遵循某些接口约定。本章节会深入 JSX 转换的细节,以及 JSX 底层使用的 React API。

注意:本章需要你了解 创建元素 的底层 API,例如 React.createElementReactDOMRe.createDOMElementVariadic

注意:本章假设你的 bsconfig.json 拥有 "reason": { "react-jsx": 3 }设置,从而能够正确应用 JSX 转换。

组件类型

普通的 React 组件被定义为 ('props) => React.element 函数。

下面是一些例子,演示了如何定义组件类型(与现有 JS 代码互操作,或传递组件时很有用):

RES
// Plain function type type friendComp = ({"name": string, "online": bool}) => React.element; // Equivalent to // ({"padding": string, "children": React.element}) => React.element type containerComp = React.component<{ "padding": string, "children": React.element }>;

上面的类型非常底层(基本上是 React 组件的 JS 表示),ReScript React 使用更富有语言特点方式来定义 React 组件,接下来让我们详细了解。

JSX 组件接口

ReScript React 组件是一个(子)模块,含有被 JSX 使用的 makemakeProps 函数。为了更好的使用体验,我们提供一个 @react.component 装饰器来为你创建这些函数:

DecoratedExpanded
module Friend = {
  @react.component
  let make = (~name: string, ~children) => {
    <div>
      {React.string(name)}
      children
    </div>
  }
}

在扩展后的输出中:

  • makeProps:一个接收多个标签参数(prop 名)的函数,返回一个被 make(props) 消耗的值。

  • make:一个转换后的 make 函数,编译到组件接口 (props) => React.element

注意makeProps 函数总会包含一个 ~key 参数。

特殊情况 React.forwardRef

@react.component 装饰器也对 React.forwardRef 生效:

DecoratedExpanded
module FancyInput = {
  @react.component
  let make = React.forwardRef((~className=?, ~children, ref_) =>
    <div>
      // use ref_ here
    </div>
  )
}

就像上面的被扩展后的输出,装饰器会转换传给 React.forwardRef 的函数,就像转换典型的组件 make 函数一样。它也会创建一个 makeProps 函数,而且会添加 ref prop,便于我们在 JSX 调用时使用(<FancyInput ref=.../>)。

现在,我们明白了 ReScript React 组件是如何转换的,让我们接着看看 ReScript 如何转换 JSX 结构。

JSX 底层原理

当我们在自定义组件使用 JSX 时(“首字母大写的 JSX”),我们实际上使用 React.createElement 去创建新元素。下面是没有子元素的 React 组件的例子:

JSXWithout JSXJS
<Friend name="Fred" age=1 />

如你所见,它使用 Friend.makeFriend.makeProps 调用 React.createElement API。如果你提供了子元素,它会调用 React.createElementVariadicReact.createElement 的一个不同绑定):

JSXWithout JSXJS Output
<Container width=200>
  {React.string("Hello")}
  {React.string("World")}
</Container>

注意,~children=React.null 属性没有意义,因为 React 只关心传递给第三个参数的子元素数组。

DOM 元素

“首字母未大写的 JSX” 表达式会被视为 DOM 元素,并被转换为 ReactDOMRe.createDOMElementVariadic 调用:

JSXWithout JSXJS Output
<div title="test"/>

有子元素的 JSX 也一样:

JSXWithout JSXJS Output
<div title="test">
  <span/>
</div>