首先jsx毫不是一個新的語言,我以爲他僅僅是js的超集,或者只是一種語法糖。本來FB是有本身的jsx-transform來負責jsx的解析,不過如今已經中止更新,jsx語法徹底依賴babel進行解析。html
babel在識別jsx的時候,會將標籤直接做爲AST的node,而後轉化node
class Square extends React.Component { render() { return ( <button className="square"> {this.props.value} </button> ); } }
將被babel轉化爲react
class Square extends React.Component { render() { return React.createElement( "button", { className: "square" }, this.props.value ); } }
這裏爲了方便和閱讀對es6的轉化,暫時忽略。git
仍是針對上面的代碼,來看一下babylon AST的解析結果,只包含return部分
整個標籤部分是JSXElement
其中會有三個部分openingElement,closingElement和children,屬性則會成爲openingElement的JSXAttributees6
jsx在babel的定義github
defineType("JSXElement", { builder: ["openingElement", "closingElement", "children", "selfClosing"], visitor: ["openingElement", "children", "closingElement"], aliases: ["JSX", "Immutable", "Expression"], fields: { openingElement: { validate: assertNodeType("JSXOpeningElement"), }, closingElement: { optional: true, validate: assertNodeType("JSXClosingElement"), }, children: { validate: chain( assertValueType("array"), assertEach( assertNodeType( "JSXText", "JSXExpressionContainer", "JSXSpreadChild", "JSXElement", "JSXFragment", ), ), ), }, }, });
AST的jsx節點最後會被替換爲createElement的形式,也就是說無論怎麼用jsx,其實也就只是一種語法糖,與createElement的寫法無異babel
爲了更好的理解jsx,那我解釋一段最近看到的代碼dom
methodsParamsList.map((ListSourceComponent, index) => { return <ListSourceComponent.component index={index + 1} /> })
核心也就是openingElement<ListSourceComponent.component index={index + 1} />
在babel的眼裏他作爲Element有兩個部分,名字和屬性,屬性很少說了。
ListSourceComponent.component會被認爲是JSXMemberExpression,ListSourceComponent是JSXIdentifier的對象component則是屬性
若是是<ListSourceComponent index={index + 1} />
那麼ListSourceComponent是JSXIdentifier的nameide
整個代碼的結果也就是ui
methodsParamsList.map((ListSourceComponent, index) => { return React.createElement(ListSourceComponent.component, { index: index + 1 }); });
其實在轉碼的階段是否將JSXMemberExpression的name或者對象的首字母大寫是不重要的,真的致使報錯的也不過是在createElement中
createElement()React.createElement(type, [props],[...children])
Create and return a new React element of the given type. The type argument can be either a tag name string (such as 'div' or 'span'), or a React component type (a class or a function).
Code written with JSX will be converted to use React.createElement(). You will not typically invoke React.createElement() directly if you are using JSX. See React Without JSX to learn more.
在剛學react的時候,老師就說過react component須要大寫,react會把大寫開口的做爲react的組件,否則則會使普通的html dom,好比div,span
首先是否是在解析ast的時候就把大小寫分開處理了,答案是否認的,在定義openingElement的type的時候徹底沒有對這個進行判斷,無論是大寫,小寫均做爲string,是element的type
可是在babel的結果裏能夠看到React.createElement('div', ...)
React.createElement( Div, ...)
這兩種結果帶來的就是Div這個是一個字符串仍是變量名,若是本身不慎寫了個小寫的component名,那麼一個並非html標籤的字符串必然會形成錯誤
這裏必須穿插一個JSX dot notation
這個和上面的例子相關就是dot notation在jsx中的解析
methodsParamsList.map((ListSourceComponent, index) => { return <ListSourceComponent.component index={index + 1}/> })
methodsParamsList.map((listSourceComponent, index) => { return <listSourceComponent.component index={index + 1}/> })
上面兩段代碼都不會致使錯誤,緣由就是在解析JSXIdentifier的結果會是一組對象和屬性,因此轉碼結果必定不是字符串而是obj.pro
上面說到了在構建ast的過程當中大小寫並不進行區分,而是在@babel/plugin-transform-react-jsx中進行變化。
關於上面的幾個轉碼的結果的解釋都在下面的源碼中
const createIdentifierParser = (id: string) => () => { return id .split(".") .map(name => t.identifier(name)) .reduce((object, property) => t.memberExpression(object, property)); };
這裏能夠發現,react支持dot notation,可是好比<A['x'] />
就必定會報錯了,雖然看起來與<A.x />
同樣