Reactのアーキテクチャを知る JSXのトランスパイル
こんにちは、noyanです。React内部実装アドベントカレンダーの6日目です。前回でuseStateが一段落したので、1回で終わる簡単な記事を書きます。
JSXはフレームワーク非依存のJavaScript構文拡張で、宣言的にUIを記述することを可能にします。多くの人は親しみがあるものの、中身について知らない人も多いのではないでしょうか。現に、React17で入ったJSXの新たな変換について「改善」として紹介しつつも、改善点を挙げていない記事がいくつか見受けられます。
もちろんJSXのトランスパイルを知らなければJSXを使えないなんてことはありません。しかしJSXの知識は、内部実装を読むにあたって必須の知識であるとともに、他
JSXの変換先
前述の通りJSXは拡張構文で、これはbabelなどを通して変換されます。Reactの場合、JSXは次のうちどちらかに変換されます。
// もともと const Hoge = () => <div>hoge</div>; // "runtime": "classic" /*#__PURE__*/React.createElement("div", null, "hoge"); // "runtime": "automatic" /*#__PURE__*/_jsx("div", { children: "hoge" });=
この_jsx
とReact.createElement
はだいたい同じことをしていますが、すでに知られているように利便性とパフォーマンスの面で_jsx
が有利です。
React.createElement
と_jsx
の違いに、_jsx
はトランスパイル時にインポート文が自動挿入されるため自分でimport文を書かなくて良いことが挙げられます。内部的にはパフォーマンスを向上させたり、spread構文でkeyが渡ることを非推奨にする準備として_jsx
を導入しているようです。
とくにkeyの取り扱いは面白くて、_jsx
では常に{...props}内にkeyがあればそれが優先されますが、
createElement
ではマージする順番でkeyが変わります。いずれkey spreadingは非推奨になる可能性があるので、多少記憶にとどめておくと良いでしょう。
// props = {key:11}のとき <div key={12} {...props}/> // key = 11 <div {...props} key={12}> // key = 12
以上の動作は次のレポジトリから確認できます。
https://github.com/noyanyan/babel-jsx-transform
JSX構文が何にトランスパイルされるかはトランスパイラの設定で変えることができます。
https://babeljs.io/docs/en/babel-preset-react#docsNav
他フレームワークのJSX変換については、babelのpragma
, tsconfigのjsxFactory
を変えることで観察できます。vueに関してはpotato4dさんの記事が勉強になります。
https://d.potato4d.me/entry/20200830-tsx-in-vue/
ReactElement
細かい処理の違いはこれで終わりにして、jsx
とcreateElement
はどちらもReactElementを返します。
これは5つのプロパティ($$typeof, type, key, ref, props, _owner)を持つオブジェクトを返します。
export function jsx(type, config, maybeKey) { return ReactElement( type, key, ref, undefined, undefined, ReactCurrentOwner.current, props, ); } const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { // This tag allows us to uniquely identify this as a React Element $$typeof: REACT_ELEMENT_TYPE, // symbolFor('react.element') // Built-in properties that belong on the element type: type, key: key, ref: ref, props: props, // Record the component responsible for creating this element. _owner: owner, }; return element; }
おわりに
本記事ではJSX構文が_jsx
を呼び出し、ReactElementを作成しているところを確認しました。
React.Elementは5つプロパティをもつオブジェクトをつくることを大仰に説明している感はありますが...。