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() 。
- 请使用
h()
,而非React.createElement,系统会自动注入h()
的实现。 - 本题目的目的并不是要再实现一个parser,所以只要满足如下最基本的Spec即可。
JSXElement: JSXOpeningElement JSXChildren? JSXClosingElementJSXOpeningElement: < JSXElementName >JSXChildren: JSXChildJSXClosingElement: < / JSXElementName >JSXChild: JSXText
- 你不必遵守上述命名
- 传入的字符串中没有换行,所以不需要实现the whitespace rules
- 仅需要支持HTML tag name即可
- 为了方便,
parse()
不会被单独测试,取而代之的parse()
和generate()
会被一起测试:
const result = eval(generate(parse('<a>bfe.dev</a>')))expect(result).toEqual(h('a', null, 'bfe.dev'))
- 如果传入的字符串是无效的JSXElement,需要throw Error。比如JSXOpeningElement和JSXClosingElement不能配对的情况等等。
测试目标并不是罗列所有无效的例子,所以仅包含一些常见的错误情况。