MarsPen / blog

3 stars 0 forks source link

【手写篇 - Day 12】Virtual DOM V - JSX1 #13

Open MarsPen opened 2 years ago

MarsPen commented 2 years ago

题目描述

如果用过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? JSXClosingElement

JSXOpeningElement:
  < JSXElementName >

JSXChildren:
  JSXChild

JSXClosingElement:
  < / JSXElementName >

JSXChild:
  JSXText

同类题目