В стандартной трансформации JSX в JavaScript используется функция
React.createElement, однако в различных средах и
библиотеках JSX может компилироваться в вызовы других фабричных функций.
В esbuild за это отвечает опция jsxFactory.
По умолчанию esbuild преобразует JSX примерно следующим образом:
const element = <div>Hello</div>
в:
const element = React.createElement("div", null, "Hello");
Такое поведение актуально только при использовании React-совместимого
режима и стандартной фабрики React.createElement.
Опция jsxFactory позволяет заменить функцию, которая
используется для создания JSX-элементов.
Это критично в случаях:
h или аналог заменяет
ReactВ esbuild настройка задаётся через API или CLI.
require("esbuild").build({
entryPoints: ["src/app.jsx"],
bundle: true,
outfile: "dist/bundle.js",
jsxFactory: "h"
})
esbuild src/app.jsx --bundle --outfile=dist/bundle.js --jsx-factory=h
Исходный код:
const App = () => <div className="box">Text</div>
При jsxFactory: "h":
const App = () => h("div", { className: "box" }, "Text");
Preact использует функцию h вместо
React.createElement.
jsxFactory: "h"
или с импортом:
import { h } from "preact";
В этом случае важно, чтобы runtime действительно содержал
h.
Некоторые библиотеки используют сигнатуру:
h(type, props, ...children)
esbuild генерирует вызовы строго в таком формате.
JSX поддерживает фрагменты, позволяющие группировать элементы без дополнительного DOM-узла:
<>
<div>A</div>
<div>B</div>
</>
В стандартной React-сборке это превращается в:
React.createElement(React.Fragment, null,
React.createElement("div", null, "A"),
React.createElement("div", null, "B")
);
Опция jsxFragment управляет тем, чем заменяется
React.Fragment.
jsxFragment задаёт идентификатор или функцию,
используемую для фрагментов JSX.
Это важно при:
require("esbuild").build({
entryPoints: ["src/app.jsx"],
bundle: true,
outfile: "dist/bundle.js",
jsxFactory: "h",
jsxFragment: "Fragment"
})
esbuild src/app.jsx --bundle --outfile=dist/bundle.js --jsx-factory=h --jsx-fragment=Fragment
Исходный код:
const App = () => (
<>
<span>1</span>
<span>2</span>
</>
);
При настройках:
jsxFactory: "h"
jsxFragment: "Fragment"
Результат:
const App = () => h(Fragment, null,
h("span", null, "1"),
h("span", null, "2")
);
Обе опции работают совместно и определяют поведение всей JSX-трансформации.
jsxFactory — создаёт элементыjsxFragment — создаёт группы элементов без
контейнераИх комбинация определяет конечный runtime-вызов.
Типичная конфигурация для React:
jsxFactory: "React.createElement",
jsxFragment: "React.Fragment"
Результат полностью совместим с React 16+.
jsxFactory: "h",
jsxFragment: "Fragment"
При этом обычно импортируется:
import { h, Fragment } from "preact";
esbuild не добавляет импорт React самостоятельно при использовании кастомных фабрик.
Если используется:
jsxFactory: "h"
необходимо явно импортировать h.
esbuild не выполняет runtime-анализ JSX. Все замены происходят на этапе компиляции.
Все JSX-элементы преобразуются в одинаковую сигнатуру:
factory(type, props, ...children)
Для фрагментов:
factory(Fragment, null, children)
При использовании TypeScript важно синхронизировать настройки компилятора:
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment"
}
}
Если используется jsx: "react-jsx" (новый runtime),
esbuild может конфликтовать с TS трансформацией, если не согласовать
runtime.
Библиотеки, заменяющие React, часто требуют кастомной фабрики:
h)createElement или h)Некоторые фреймворки используют JSX как синтаксический сахар для прямых DOM-вызовов.
Пример:
jsxFactory: "dom"
jsxFragment: "fragment"
JSX может использоваться не только для UI, но и для генерации структур:
<query>
<select>name</select>
</query>
Тогда jsxFactory может быть любой функцией,
например:
jsxFactory: "createNode"
Выбор jsxFactory и jsxFragment напрямую
влияет на:
Минимальные фабрики (h) обычно дают более компактный
результат, чем React.createElement.
Если указана строка:
jsxFactory: "h"
но h не существует в runtime, возникнет
ReferenceError.
Если используется:
jsxFragment: "Fragment"
но Fragment не импортирован, фрагменты ломают
выполнение.
TypeScript с jsx: react-jsx уже генерирует вызовы к
_jsx и _Fragment, что может конфликтовать с
esbuild, если одновременно заданы кастомные фабрики.