React JSXの実現とwebpackなどの細かい調査
こんにちは。 Showcase Gig でエンジニアをしている、ryoです。
最近O:der Platformの開発でReactを使いながらいろいろ勉強をしました。 今日は、React入り口のJSXをもとに、簡単なコードからコード裏に潜んだdesign方針までを説明します。
syntactic sugar
simple code
先に簡単なReact component codeです: App.tsx
import React from 'react';
export default function App(): JSX.Element {
return (
<div>syntactic sugar for typescript</div>
);
}
上のコードはまったくReactを使っていないのに、なぜ最初Reactをimportする必要( import React from 'react' )があるかっていう疑問が初めてReactを書いた時自然に湧いてきました。
import文をコメントアウトしたら、エラーになります。
もともと、Web browserはjsしか識別できないから、先にwebpackなどでtsxをjsにtranspileしないといけません。 transpile後のコードを確認すると、赤枠通りtsxはjsにtranspileされていました。
tsxにある <div>syntactic sugar for typescript</div> はjsにtranspileされました。
react_1.default.createElement(\"div\", null, \"syntactic sugar for typescript\"));
引き続き調査したら、 react_1.default はReactであると分かりました。
var react_1 = __importDefault(__webpack_require__(/*! react */ \"./node_modules/react/index.js\"));
まとめると次のようになります。
1. JSX(tsx)は React.createElement にtranspileされます。
2. JSXは React.createElement のSyntactic Sugarです。
jsの場合
前述のはすべてtsですが、jsだったら、エラーは少し違います。
参考になったlink
・Introducing JSX
・JSX is syntactic sugar
React 17以降
前述のsimple codeの場合は import React from 'react' がいらなくなります。 例えば:
function App(): JSX.Element {
return <h1>Hello World</h1>;
}
Babelは下の形にtranspileします。
import {jsx as _jsx} from 'react/jsx-runtime';
function App() {
return _jsx('h1', { children: 'Hello world' });
}
設計
IoC:
もともとfunction componentはfunctionとしても呼び出せます。例えば: <div>{App()}</div> 。だがそうすると、Reactが部品を一般のfunctionとして識別します。 この場合、Reactがfunctionのmetadataを何も知らなくて何もできなくなってしまいます。これは開発者のみが部品をコントロールするパターンになります。
逆にJSX( React.createElement )として使われるなら、部品の情報はReactが全て把握でき、 Reactがいろいろできるようになります。例えば「部品のタイプが違ったら、Reactはpropsを比較せず、古い部品を入れ替える」といったことができます。 これはライブラリ(React)が部品をコントロールするパターンです。コントロールを一気に開発者からReactに逆転させました。
Dan Abramove氏の説明
inversion of control。日本語の翻訳がないので、一部訳してみます。 なぜ部品を直接に呼ばないのか ? Form() より <Form /> の方が使われるのはなぜでしょうか ? 再帰的にfunctionを呼び出してReact elementの階層のみより、部品の詳細まで把握できた方が、Reactはもっと良くやってくれます。
// NG: ReactはLayoutとArticleの存在は知らない.
// 開発者はfunctionとして呼び出します。
ReactDOM.render(
Layout({children: Article()}),
domContainer
)
// OK: ReactはLayoutとArticleは把握します。
// Reactは部品を呼び出します。
ReactDOM.render(
<Layout><Article></Article></Layout>,
domContainer
)
これはIoCの良くあった例です。部品の呼び出しをReactに預けたら、複数のおもしろい特性を得られます。
部品はfunctionのみではない
Reactは部品の機能を増強できます。例えば:部品のidエンティティと紐づいているローカルstate。 良いruntimeは手元にある課題と一致する基本的な抽象を与えてくれます。 Reactはui treesのレンダリングとinteractionへの反応向けですので、直接部品を呼ぶと、これらのフィーチャーは自ら実装しないといけません。
部品のタイプはreconciliationに使われます
部品の呼び出しをReactに預けたら、部品treeの構造もReactに知らせることができます。 例えば、 <Feed> のレンダリングから <Profile> のレンダリングに切り替えたら、Reactは <Feed> 部品内のものを再利用しないで、すぐ <Profile> をレンダリングします。 コンセプト上に違ったviewをレンダリングするのにこれは良い案です。
Reactはreconciliationを後回しできます
Reactが部品の呼び出しをコントロールできたら、いろいろおもしろいことはできます。 例えば:部品の呼び出しの間に、ブラウザーにほかのことをやらせますので、大きな部品treeのre-renderingはmain threadをブロックしません。 この機能を成し遂げるのにReactの大部分を再度実現しない限りは難しいです。
もっと良くdebugできます
部品はライブラリが識別できるfirst-class citizensになったら、開発者に使われるrich developer toolsは作られます。
まとめ
JSXはapp開発者が使いやすくするために導入されたものです。しかし、Web browserが識別できないので、ほかのツール(webpackなど)で識別できるコードに書き換えられていたのです。
最後に
React経験はまだまだ浅いですが、今後もReactに関するものをシェアしていきたいです!
Showcase Gigで急成長するプロダクトを一緒に作る仲間を募集しています!
この記事が気に入ったらサポートをしてみませんか?