143. Virtual DOM IV - JSX 1

中等难度  -通过 / -执行

如果用过React的话,JSX应该是非常熟悉。

通过支持JSX syntax,transpiler可以在JavaScript代码中直接理解如下这种非标准的语法。

<p> this is <button className="button">button</button> </p>

理解过后可以生成标准的JavaScript代码。

React.createElement("p", null,
  " this is ",
  React.createElement("button", { className: "button" }, "button"),
  " ");

可以在TypeScript Playground中试试看。

Transpiler是如何工作的?我们从一个简单的例子来看看。

<a>bfe.dev</a>

首先,上述的代码会被解析成AST(Abstract Syntax Tree)。

打开AST Explorer, 输入上述代码,在右方即可看见AST,大概是这样的构造:

expression: JSXElement {
  openingElement: JSXOpeningElement {
    name: JSXIdentifier {
      name: "a"
    }
  }
  closingElement: JSXClosingElement {
    name: JSXIdentifier {
      name: "a"
    }
  }
  children: [
    JSXText {
      value: "bfe.dev"
    }
  ]
}

简单易懂对吧?实际上就是JSX Spec定义的那样:

JSXElement:
  JSXOpeningElement JSXChildren? JSXClosingElement

JSXOpeningElement:
  < JSXElementName JSXAttributes? >

JSXChildren:
  JSXChild JSXChildren?

JSXClosingElement:
  < / JSXElementName >

JSXChild:
  JSXText
  JSXElement
  { JSXChildExpression? }

通过遍历上述AST,然后在合适的地方插入React.createElement即可得到转换后的代码。

React.createElement("p", null,
  " this is ",
  React.createElement("button", { className: "button" }, "button"),
  " ");

我们也可以不用React的方法,而使用140. Virtual DOM III - Functional Component中定义的h()

h("p", null,
  " this is ",
  h("button", { className: "button" }, "button"),
  " ");

现在,请实现能够解析并转换JSX Element字符串的parse()和generate()

  1. 请使用h(),而非React.createElement,系统会自动注入h()的实现。
  2. 本题目的目的并不是要再实现一个parser,所以只要满足如下最基本的Spec即可。
JSXElement:
  JSXOpeningElement JSXChildren? JSXClosingElement

JSXOpeningElement:
  < JSXElementName >

JSXChildren:
  JSXChild

JSXClosingElement:
  < / JSXElementName >

JSXChild:
  JSXText
  • 你不必遵守上述命名
  • 传入的字符串中没有换行,所以不需要实现the whitespace rules
  • 仅需要支持HTML tag name即可
  1. 为了方便,parse()不会被单独测试,取而代之的parse()generate()会被一起测试:
const result = eval(generate(parse('<a>bfe.dev</a>')))
expect(result).toEqual(h('a', null, 'bfe.dev'))
  1. 如果传入的字符串是无效的JSXElement,需要throw Error。比如JSXOpeningElement和JSXClosingElement不能配对的情况等等。

    测试目标并不是罗列所有无效的例子,所以仅包含一些常见的错误情况。

注意特例。

(3)
(20)