143. Virtual DOM IV - JSX 1
シェアしよう
少し工夫が必要 - accepted / - tried
Reactを使っているなら、JSXは馴染みがあるでしょうか。
JSX syntaxをサポートすることで、transpilerはJavaScriptの中でもこんなスタンダードではない文法を理解することができる。
<p> this is <button className="button">button</button> </p>
理解してスタンダードのJavaScript functionに変換することができる。
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を持って、JavaScriptコードを生成するのは割と簡単、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()を実装してください。.
- React.createElementじゃなく、
h()
を使ってください。h()
はテスト時バンドルされる。 - parserを再実装する目的ではないので、下記のミニマムのスペックを満たせば良い:
JSXElement: JSXOpeningElement JSXChildren? JSXClosingElementJSXOpeningElement: < JSXElementName >JSXChildren: JSXChildJSXClosingElement: < / JSXElementName >JSXChild: JSXText
- 上記のnamingは従わなくてよい
- 渡す文字列には改行がない、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文字列が渡されたらエラーを吐くベキ、例えばJSXOpeningElement と JSXClosingElement がペアになっていない場合など。
テストケースは全てのエラーケースを網羅する目的ではないので、一部だけカバーしている