JavaScript XML

JavaScript XML
パラダイムドメイン固有言語、言語拡張
家族ECMAScriptXML
デザイン:フェイスブック
開発者フェイスブック
初登場2014年8月29日[ 1 ] ( 2014年8月29日)
タイピングの規律弱い、ダイナミック
ファイル名拡張子.jsx.tsx
Webサイトgithub.com /facebook / jsx
主要な実装
バベル
方言
反応する

JSX ( JavaScript XMLと呼ばれることもある)は、JavaScript言語構文のXMLのような拡張です。[ 2 ]当初はFacebookがReactで使用するために作成しましたが、JSXは複数のウェブフレームワークに採用されています。[ 3 ] : 5 [ 4 ] : 11 構文糖衣であるJSXは、通常、元のJSXと構造的に類似したネストされたJavaScript関数呼び出しにトランスパイルされます

TypeScriptで使用する場合、ファイル拡張子は.tsxになります。[ 5 ]

プロセス

JSXで書かれたコードは、Webブラウザで理解できるようになる前に、Babelなどのツールで変換する必要がある。 [ 6 ] [ 7 ]:5 この処理は通常、アプリケーションがデプロイされる前のソフトウェアビルドプロセス中に実行される。

トランスパイラ(Babelなど)は、JSXの使用の性質について仮定を立てなければなりません。そうでなければ、JSXタグを何に変換すればよいか分かりません。Babelの場合(下記)、タグがreactで使用されることを前提としたモードで使用するため、JSXタグはReact JSXランタイムへの関数呼び出しに変換され、これらのタグの内部表現に対応する値を返します。[ 8 ] BabelがJSXに使用するは、と@babel/plugin-transform-react-jsxの役割を担う代替プラグマを指定できるようにすることで、他のプラットフォームに適応するための柔軟性を提供しています。[ 8 ]React.createElementReact.Fragment

マークアップ

次の例には、React-JSX/TSX コードと Babel からのトランスパイルされた出力の両方が含まれています。

基本的な例

TSXコード トランスパイルされた出力(Babel)
'react'からReactをインポートしますconst App : React.FC = () => { return ( < div > < p >ヘッダー</ p > < p >コンテンツ</ p > < p >フッター</ p > </ div > ); }デフォルトアプリをエクスポートします
import React from 'react' ; import { jsx as _jsx , jsxs as _jsxs } from "react/jsx-runtime" ; const App = () => { return /*#__PURE__*/ _jsxs ( "div" , { children : [ /*#__PURE__*/ _jsx ( "p" , { children : "Header" }), /*#__PURE__*/ _jsx ( "p" , { children : "Content" }), /*#__PURE__*/ _jsx ( "p" , { children : "Footer" })] }); }; export default App ;

Babelによって追加されたコメント/*#__PURE__*/は、タグ付けされた呼び出しが純粋関数への呼び出しであることを示しています。これは、ツリーシェイキングなどの後のステップに関係しています。[ 9 ]これはコードの機能には必要ないため、次の例では読みやすくするためにこの注釈が削除されています。

上記のトランスパイラ出力はReact 17以降からの出力を示しています。それ以前のバージョンでは、出力はより単純なものになります。

const App = () = > { return React.createElement ( "div" null React.createElement ( "p" null "Header" )、React.createElement ( " p" null " Content" )、React.createElement ( " p " null " Footer" )) ; } ; export default App ;

クラシック インターフェースは、React 以外の JSX ユーザーによって引き続き広く使用されています。

ネストされた要素

同じレベルにある複数の要素は、上記の要素のように単一の要素で囲むか、またはその省略形で<div>区切られたフラグメント、または配列として返す必要があります。[ 10 ] [ 11 ] [ 4 ]:68–69 <Fragment><>

属性

JSXは、HTMLが提供する属性を反映するように設計された様々な要素属性を提供します。カスタム属性をコンポーネントに渡すこともできます。[ 12 ]すべての属性は、コンポーネントによってprops(プロパティ)として受け取られます。

有効なJSX属性名は有効なJSX識別子(変数名)であり、ECMAScript識別子との主な違いは、-先頭以外の位置に(ハイフン-マイナス)文字を追加で使用できることです。これにより、 や などのダッシュを含むHTML属性をdata-*容易aria-roleに使用できます。無効な文字がまだ含まれている属性は、構文に従って記述できます{... spread}[ 2 ]

TSXコード トランスパイルされた出力(Babel)
< a href = "https://en.wikipedia.org" data-note = "Wikipedia" { ... { ": tricky - property - name ": a }} // XMLプロパティにはコロンが含まれコロン始まる場合があります> Wikipedia </ a >;
React.createElement ( "a" , { href : "https://en.wikipedia.org" , "data - note" : "Wikipedia" , ":tricky-property-name" : a , }, " Wikipedia" , );

カスタムコンポーネント

カスタム「タグ」(コンポーネント) は、特定の呼び出しスタイルを受け入れる JavaScript 関数です。

JSXコード トランスパイルされた出力(Babel)
クラスHello はComponentを拡張します{ render () { return < h1 > Hello , { this . props . name } ! </ h1 >; } }function Jello ({ name }) { return < h1 > Jello , { name } ! </ h1 >; }エクスポートデフォルト関数App () { return ( <> < Hello name = "Sarah" /> < Jello name = "Connor" /> </> ); }
クラスHello はComponent拡張します{ render ( ) { return React.createElement ( "h1" , null , " Hello, " , this.props.name , " ! " ) ; } }function Jello ({ name }) { return React . createElement ( "h1" , null , "Jello, " , name , "!" ); }function App () { return React.createElement ( React.Fragment , null , React.createElement ( Hello , { name : " Sarah" , } ) , React.createElement ( Jello , { name : " Connor " , } ) , ) ; }

ここではname="Connor"単にオプションになります{ name: "Connor" }

childrenラップ コンポーネントを含むコンポーネントは、トランスパイルされた出力ですでに確認した属性 を使用して定義できます。

JSXコード トランスパイルされた出力(Babel)
const Wrapper = ( props ) => { return ( < div style = {{ backgroundColor : 'lightgray' , padding : '20px' , border : '1px solid black' }}> < h2 >ラッパー内部: </ h2 > { props . children } </ div > ); } ; < Wrapper > < p >段落</ p > < p >もう1 つ! </ p > </ Wrapper >
const Wrapper = ( props ) => { return React.createElement ( "div" , { style : { backgroundColor : "lightgray" , padding : "20px" , border : "1px solid black" , } , }, React.createElement ( "h2" , null , "ラッパー内部: " ) , props.children , ) ; } ;React.createElement ( Wrapper null React.createElement ( "p" null "段落" )、React.createElement ( " p" null "もう1 つ!" ) );

有効なJSXタグ名は有効なJSX識別子(変数名)であるため、ハイフンで区切られたXMLタグやHTMLタグも問題なく使用できます。ただし、ECMAScriptの文法上の制限により、カスタムコンポーネントの名前にはハイフンを使用できません。[ 2 ]

JavaScript式

JavaScriptのではない)は、JSX内で中括弧を使って使用できます{}: [ 4 ] : 14–16

JSXコード トランスパイルされた出力(Babel)
< h1 >{ 10 + 1 } h1 >
React.createElement ( "h1 " null 10 + 1 ) ;

上記の例では次のようにレンダリングされます:

< h1 > 11 </ h1 >

条件式

JSX内ではif-else文は使用できませんが、代わりに条件式を使用できます。以下の例は、が1に等しい ため、文字列としてレンダリングされます。{i===1?'true':'false'}'true'i

TSXコード トランスパイルされた出力(Babel)
'react'からReactをインポートしますconst App : React.FC = () => { const i : number = 1 ;戻り値( < div > < h1 > { i === 1 ? 'true' : 'false' h1 > </ div > ); }デフォルトアプリをエクスポートします
'react'からReactをインポートします。const App = ( ) => { const i = 1 ; return ( React . createElement ( "div" null React . createElement ( "h1" null i === 1 ? "true" : "false" ) ) ; }; export default App ;

上記は次のようにレンダリングされます:

< div > < h1 ></ h1 > </ div >

関数とJSXは条件文で使用できます: [ 4 ] : 88–90

TSXコード トランスパイルされた出力(Babel)
'react'からReactをインポートしますconst App : React.FC = () => { const sections : number [] = [ 1 , 2 , 3 ];return ( < div > { sections . map (( n , i ) => ( /* 'key' はリスト項目とその変更を追跡するための React 固有の属性です */ /* 各 'key' は一意である必要があります */ < div key = { "section-" + n }> Section { n } { i === 0 && < span >( first )</ span >} </ div > ))} </ div > ); }デフォルトアプリをエクスポートします
'react'からReactをインポートしますconst App = () = > { const sections = [ 1 , 2 , 3 ] ; return React.createElement ( "div" , null , sections.map ( ( n , i ) = > React.createElement ( "div" , { key : "section-" + n } , "Section " , n , " " , i === 0 && React.createElement ( "span" , null , " (first)" ) , ) , ), ); };デフォルトアプリをエクスポートします

上記は次のようにレンダリングされます:

< div > < div >セクション 1 < span > (最初) </ span ></ div > < div >セクション 2 </ div > < div >セクション 3 </ div > </ div >

バベル:プラグマ

Babelなどのトランスパイラは、React以外の関数呼び出しをターゲットにできる場合が多いです。ここではBabelは次のように呼び出しtagfrag代わりに次のように指示されます。

TSXコード トランスパイルされた出力(Babel)
// @jsx タグ// @jsxfrag frag const App = () => { const sections = [ 1 , 2 , 3 ]; return ( < div > { sections . map (( n , i ) => ( < div key = { "section-" + n }> Section { n } { i === 0 && < span >( first )</ span >} </ div > ))} </ div > ); };
// @jsx タグ// @jsxfrag frag const App = () => { const sections = [ 1 , 2 , 3 ]; return tag ( "div" , null , sections . map (( n , i ) => tag ( "div" , { key : "section-" + n }, "Section " , n , " " , i === 0 && tag ( "span" , null , "(first)" )), ), ); };

と の異なるバージョンを使用するとtagfrag入力の異なる表現を生成できます。

JSコード プログラム出力
// オブジェクト ツリー。//フラグメントは配列として表されます。function frag (... children ) { return children ; }// タグはオブジェクトとして表現されますfunction tag ( name , attrs , ... children ) { // 配列をフラット化するとフラグメントが展開されます// それでも "false" を処理する必要がありますconst kids = [] for ( x of children . flat ( Infinity )) { if ( x === false || x === null || x === undefined ) { if ( kids . at ( - 1 ) === " " ) kids . pop () } else kids . push ( x ) } return { name , children : kids , ...( attrs && { attrs })} }console.log ( JSON.stringify ( App ( ) ) ) ;
{ "name" : "div" , "children" : [ { "name" : "div" , "attrs" : { "key" : "section-1" }, "children" : [ "セクション " , 1 , " " , { "name" : "span" , "children" : [ "(first)" ] } ] }, { "name" : "div" , "attrs" : { "key" : "section-2" }, "children" : [ "セクション " , 2 ] }, { "name" : "div" , "attrs" : { "key" : "section-3" }, "children" : [ "セクション " , 3 ] } ] }
// Pug のような出力関数frag (... children ) { let kids = []; for ( const x of children ) { // テキストは | で始まるif ( ! Array . isArray ( x )) kids . push ( "| " + x ); else kids . push (... x . flat ( Infinity )); } }function tag ( name , attrs , ... children ) { const indent = " " ; const attrStr = Object . entries ( attrs || {}) . map (([ k , v ]) => ` ${ k } =" ${ v } "` ) . join ( ", " ); const head = ` ${ name }${ attrStr ? `( ${ attrStr } )` : "" } ` ; let kids = []; for ( const x of frag ( children )) { if ( x === false ) { if ( kids . at ( - 1 ) === "| " ) kids . pop (); } else kids . push ( x ); } if ( kids . length == 1 ) // elem>text および要素のネストのための省略形構文return kids [ 0 ]. startsWith ( "| " ) ? [ ` ${ head } ${ kids [ 0 ] . substr ( 2 ) } ` ] : [ ` ${ head } : ${ kids [ 0 ] } ` ]; return [ head , ... kids . map (( line ) => indent + line )]; }コンソール.log ( App (). join ( '\n' ) );
div div (キー= "section-1" ) | セクション | 1 | span (最初) div (キー= "section-2" ) | セクション | 2 div (キー= "section-3" ) | セクション | 3 

方言

上記はJSXからECMAScriptコードへの変換について説明しています。「プラグマ」セクションで示されているように、JSX派生オブジェクトを受け取るECMAScriptコードは、これらのデータに対して自由に処理を行うことができます。

  • Reactなどのフロントエンドアプリケーションは、通常、これらのデータ構造をページのドキュメントオブジェクトモデルを操作する関数呼び出しに変換します。これらのライブラリには、サーバーサイドレンダリングのバリアントも用意されており、「ブラウザレス」DOMを操作することで、DOMをシリアル化し、通常のHTMLまたはXMLウェブページを生成します。[ 13 ]
  • バックエンドアプリケーションは、文字列をつなぎ合わせてHTMLまたはXMLを直接生成することを選択し、実質的にテンプレートエンジンとして機能する場合があります。[ 14 ]
  • よりニッチなアプリケーション (上記で示したようなもの) は、HTML や XML とはまったく関係がない場合があります。

HTML と XML が関係する限り、相違が最もよく見られる領域は、属性の処理と利用可能なコンポーネントの設定です。

属性

名前

JSXは属性の解析方法を定義し、Babelはそれを他のECMAScriptコードにどのように伝えるかについての規約を維持していますが、これらの属性が実際のHTMLおよびXML属性にどのようにマッピングされるかは定義していません。例えば、Reactは{... spread}構文の必要性を最小限に抑えるために、以下の規約を使用しています。[ 15 ]

  • プレーンな英数字の属性名は変更されません。
  • data-*HTMLのハイフンでつながれた属性名は、とを除き、キャメルケースに変換されますaria-*。これはJavaScript DOM APIに準拠しています。
  • 属性xlink:*名は camelCase に変換されます。たとえば、xink:hrefになりますxlinkHref

ReactのDOMレンダラーには、認識可能な属性のプリセットリストがあります。これらの属性の一部はHTML/XMLへの変換用であり、その他は内部使用用です。本番環境(非開発環境)では、タグが「svg」または「math」でない限り、出力タグにはこれらの属性のみを使用できます。[ 16 ]

価値観

HTML属性は、それを持つ要素にCSSstyleスタイルのコレクションを適用します。人間工学的な配慮として、React-DOMはこの値を文字列形式だけでなくJavaScriptオブジェクトとしても渡すことができます。 の代わりにと記述することもできます。「style」関数の実装により、オブジェクトは自動的に適切な文字列形式に変換されます。同様のキャメルケースへの名前変更操作は、ハイフンで区切られた名前を持つスタイルの使用を簡素化するために行われます。[ 17 ]<div style={"color:" + clr}><div style={{ color: clr }}>

React-DOMでは、イベントハンドラ属性の値として文字列ではなくJavaScript関数を渡すこともできます。[ 17 ]

コンポーネント

出力言語に応じて、HTML/XMLの「タグ」のように表示される異なるコンポーネントが利用できる場合があります。例えば、ReactのHTMLタグは から取得されますがreact-dom、これは異なる言語をターゲットとする場合は使用されません。ランタイムはいくつかのコンポーネントを事前定義する場合もあります。<Fragment>はその一例ですが、Babelの規約により汎用化されています。[ 18 ]

参照

参考文献

  1. ^ 「ウェブサイトの最初のコミット · facebook/jsx@c40e9b6」。GitHub
  2. ^ a b c「ドラフト: JSX仕様」 . JSX . Facebook . 2018年4月7日閲覧
  3. ^ラーセン、ジョン (2021). React Hooks in Action with Suspense and Concurrent Mode . Manning. ISBN 978-1720043997
  4. ^ a b c d Wieruch, Robin (2018年9月14日). The Road to React . Leanpub. ISBN 978-1720043997
  5. ^ 「ドキュメント - JSX」 . www.typescriptlang.org . 2025年7月13日閲覧
  6. ^フィッシャー、ルドヴィコ (2017年9月6日). React for Real: フロントエンドコードを紐解く. Pragmatic Bookshelf. ISBN 9781680504484
  7. ^ラーセン、ジョン (2021). React Hooks in Action with Suspense and Concurrent Mode . Manning. ISBN 978-1720043997
  8. ^ a b "@babel/plugin-transform-react-jsx · Babel" . babeljs.io .
  9. ^ "compiler-notations-spec/pure-notation-spec.md at main · javascript-compiler-hints/compiler-notations-spec" . GitHub .
  10. ^ Clark, Andrew (2017年9月26日). 「React v16.0§新しいレンダリング戻り値型:フラグメントと文字列」 . React Blog .
  11. ^ 「React.Component: render」 . React .
  12. ^ Clark, Andrew (2017年9月26日). 「React v16.0§カスタムDOM属性のサポート」 . Reactブログ.
  13. ^ "createRoot – React" . react.dev .
  14. ^ "html/benchmarks at master · kitajs/html" . GitHub .
  15. ^ 「React DOM コンポーネント – React」 . react.dev .
  16. ^ "react/packages/react-dom-bindings/src/client/ReactDOMComponent.js at 64b4605cb82367fa78a08b99bbee1c800e6af21a · facebook/react" . GitHub .
  17. ^ a b「共通コンポーネント(例)– React」 . react.dev .
  18. ^ 「組み込みReactコンポーネント – React」 . react.dev .