草案 / 2022 年 8 月 4 日

JSX

简介

JSX 是 ECMAScript 的一种类似 XML 的语法扩展,没有任何定义的语义。它不打算由引擎或浏览器实现。它不是将 JSX 纳入 ECMAScript 规范本身的提案。 它旨在被各种预处理器(转换器)使用,以将这些令牌转换为标准 ECMAScript。

// Using JSX to express UI components
var dropdown =
  <Dropdown>
    A dropdown list
    <Menu>
      <MenuItem>Do Something</MenuItem>
      <MenuItem>Do Something Fun!</MenuItem>
      <MenuItem>Do Something Else</MenuItem>
    </Menu>
  </Dropdown>;

render(dropdown);

原理

本规范的目的是定义一种简洁而熟悉的语法,用于定义具有属性的树结构。一种通用但定义良好的语法使独立的解析器和语法高亮器社区能够符合单一规范。

在现有语言中嵌入新语法是一项冒险的尝试。其他语法实现者或现有语言可能会引入另一个不兼容的语法扩展。

通过一个独立的规范,我们使其他语法扩展的实现者在设计自己的语法时更容易考虑 JSX。 这有望使各种新的语法扩展能够共存。

我们的目的是声明最少的语法空间,同时保持语法的简洁和熟悉。 这样,我们就为其他扩展打开了大门。

本规范不尝试遵守任何 XML 或 HTML 规范。 JSX 被设计为 ECMAScript 功能,与 XML 的相似之处仅是为了熟悉。

1 JSX 定义

1.1 修改后的产生式

JSX 扩展了PrimaryExpression (原始表达式)ECMAScript (ECMA-262) 语法中

语法

PrimaryExpression (原始表达式) : JSXElement JSXFragment

1.2 JSX 元素

语法

JSXElement : JSXSelfClosingElement JSXOpeningElement JSXChildrenopt JSXClosingElement JSXSelfClosingElement : < JSXElementName JSXAttributesopt / > JSXOpeningElement : < JSXElementName JSXAttributesopt > JSXClosingElement : < / JSXElementName > JSXFragment : < > JSXChildrenopt < / > JSXElementName : JSXIdentifier JSXNamespacedName JSXMemberExpression JSXIdentifier : IdentifierStart JSXIdentifier IdentifierPart JSXIdentifier [没有WhiteSpace (空白符)或者Comment (注释)这里] - 注意
的语法JSXIdentifier没有以相同的方式参数化Identifier (标识符)是。 这意味着当推导期间出现“[await]”时,<await /> 仍然是有效的产生式。
JSXNamespacedName : JSXIdentifier : JSXIdentifier JSXMemberExpression : JSXIdentifier . JSXIdentifier JSXMemberExpression . JSXIdentifier

1.2.1 静态语义:早期错误

JSXElement : JSXOpeningElement JSXChildrenopt JSXClosingElement

1.3 JSX 属性

语法

JSXAttributes : JSXSpreadAttribute JSXAttributesopt JSXAttribute JSXAttributesopt JSXSpreadAttribute : { ... AssignmentExpression (赋值表达式) } JSXAttribute : JSXAttributeName JSXAttributeInitializeropt JSXAttributeName : JSXIdentifier JSXNamespacedName JSXAttributeInitializer : = JSXAttributeValue JSXAttributeValue : " JSXDoubleStringCharactersopt " ' JSXSingleStringCharactersopt ' { AssignmentExpression (赋值表达式) } JSXElement JSXFragment JSXDoubleStringCharacters :: JSXDoubleStringCharacter JSXDoubleStringCharactersopt JSXDoubleStringCharacter :: JSXStringCharacter 但不是" JSXSingleStringCharacters :: JSXSingleStringCharacter JSXSingleStringCharactersopt JSXSingleStringCharacter :: JSXStringCharacter 但不是'

1.4 JSX 子元素

语法

JSXChildren : JSXChild JSXChildrenopt JSXChild : JSXText JSXElement JSXFragment { JSXChildExpressionopt } JSXText :: JSXTextCharacter JSXTextopt JSXTextCharacter :: JSXStringCharacter 但不是以下之一{或者<或者>或者} JSXChildExpression : AssignmentExpression (赋值表达式) ... AssignmentExpression (赋值表达式)

1.5 JSX 字符串

1.5.1 JSX 字符串字符

历史上,字符串字符在JSXAttributeValueJSXText中扩展为允许存在HTML 字符引用以便更容易地在 HTML 和 JSX 之间复制粘贴,但代价是不支持 \EscapeSequence (转义序列)的 ECMAScriptStringLiteral (字符串字面量)。 我们将来可能会重新考虑此决定。

语法

JSXStringCharacter :: SourceCharacter (源字符) 但不是以下之一HTMLCharacterReference

1.5.2 HTML 字符引用

注意 1

这里的语法遵循 HTML 现行标准

语法

HTMLCharacterReference :: HTMLDecimalCharacterReference HTMLHexCharacterReference HTMLNamedCharacterReference HTMLDecimalCharacterReference :: & # DecimalDigits (十进制数字) 但前提是DecimalDigits (十进制数字)≦ 0x10FFFF ; HTMLHexCharacterReference :: & # x HexDigits (十六进制数字) 但前提是HexDigits (十六进制数字)≦ 0x10FFFF ; HTMLNamedCharacterReference :: & HTMLNamedCharacterReferenceName ; HTMLNamedCharacterReferenceName :: HTML4 标准中定义的 252 个字符实体引用列表 注意 2

这意味着自 HTML5 以来,此列表后来扩展到 2231 个命名字符引用 被有意地从支持中排除

可以在这里找到 HTML4 标准中定义的 252 个字符实体引用列表。

1.5.3 JSX 字符串定义

语法

JSXString : JSXDoubleStringCharacters JSXSingleStringCharacters JSXText

1.5.3.1 静态语义:SV

实现可以将JSXString转换为 ECMAScript 字符串值。 为了做到这一点,JSX 扩展了语法导向操作 SVJSXStringCharacter:

满足扩展语义的确切方法是实现定义的

注意
例如,在撰写本文时,Babel 在其 JSX 解析器中实现了此功能,而 TypeScript 通过 JSX 转换器实现了它。

A 为什么不用模板字面量?

ECMAScript 第 6 版 (ECMA-262) 引入了模板字面量,旨在用于在 ECMAScript 中嵌入 DSL。 为什么不直接使用它,而是发明一种不属于 ECMAScript 的语法?

模板字面量非常适合用于长时间嵌入的 DSL。 不幸的是,当您在范围内使用标识符进出嵌入的任意 ECMAScript 表达式时,语法噪音非常大。

// Template Literals
var box = jsx`
  <${Box}>
    ${
      shouldShowAnswer(user) ?
      jsx`<${Answer} value=${false}>no</${Answer}>` :
      jsx`
        <${Box.Comment}>
         Text Content
        </${Box.Comment}>
      `
    }
  </${Box}>
`;

可以使用模板字面量作为语法入口点,并更改模板字面量内部的语义,以允许可以在范围内评估的嵌入式脚本。

// Template Literals with embedded JSX
var box = jsx`
  <Box>
    {
      shouldShowAnswer(user) ?
      <Answer value={false}>no</Answer> :
      <Box.Comment>
         Text Content
      </Box.Comment>
    }
  </Box>
`;

但是,这将导致进一步的分歧。围绕模板字面量强加的假设构建的工具将无法正常工作。 这将破坏模板字面量的含义。 无论如何,都需要定义 JSX 在模板字面量中 ECMAScript 语法其余部分中的行为方式。

因此,最好将 JSX 作为一种全新的 PrimaryExpression (原始表达式) 引入

// JSX
var box =
  <Box>
    {
      shouldShowAnswer(user) ?
      <Answer value={false}>no</Answer> :
      <Box.Comment>
         Text Content
      </Box.Comment>
    }
  </Box>;
注意
你不喜欢这里的语法高亮吗? ;)

B 为什么不用 JXON?

另一种选择是使用对象初始化器(类似于 JXON)。 不幸的是,平衡的大括号无法为大型树中元素的开始和结束位置提供良好的语法提示。 平衡的命名标签是 XML 风格表示法的关键语法特征。

C 现有技术

JSX 语法类似于 E4X 规范 (ECMA-357)。 E4X 是一个具有深远语义含义的已弃用规范。 JSX 部分地与 E4X 语法的一个小子集重叠。 但是,JSX 与 E4X 规范无关。

D 许可

版权所有 (c) 2014 - 至今,Meta Platforms, Inc. 保留所有权利。

本作品根据 Creative Commons Attribution 4.0 International License 获得许可。