JSX
Beschreibung
JSX ist eine Syntaxerweiterung für JavaScript, die es ermöglicht, HTML-ähnliche Strukturen direkt im JavaScript-Code zu verwenden. Es ist kein integraler Bestandteil des JavaScript-Standards und für die Erstellung von Anwendungen nicht zwingend erforderlich, kann aber je nach persönlichen Vorlieben oder den Präferenzen des Teams die Entwicklung vereinfachen und übersichtlicher gestalten.
function MyComponent() {
return {
view: () => m('main', [m('h1', 'Hello world')]),
};
}
// kann geschrieben werden als:
function MyComponent() {
return {
view: () => (
<main>
<h1>Hello world</h1>
</main>
),
};
}
Bei der Verwendung von JSX können JavaScript-Ausdrücke innerhalb von JSX-Tags durch geschweifte Klammern interpoliert werden:
var greeting = 'Hello';
var url = 'https://google.com';
var link = <a href={url}>{greeting}!</a>;
// ergibt <a href="https://google.com">Hello!</a>
Komponenten können entweder durch Großschreibung des ersten Buchstabens des Komponentennamens oder durch Zugriff auf sie als Eigenschaft verwendet werden:
m.render(document.body, <MyComponent />)
// equivalent to m.render(document.body, m(MyComponent))
<m.route.Link href="/home">Go home</m.route.Link>
// equivalent to m(m.route.Link, {href: "/home"}, "Go home")
Einrichtung
Der einfachste Weg, JSX zu verwenden, ist die Integration mit einem Babel-Plugin.
Babel benötigt npm, das automatisch installiert wird, wenn Sie Node.js installieren. Sobald npm installiert ist, erstellen Sie einen Projektordner und führen Sie den folgenden Befehl aus:
npm init -y
Wenn Sie Webpack und Babel zusammen verwenden möchten, fahren Sie mit dem nächsten Abschnitt fort.
Um Babel als eigenständiges Tool zu installieren, verwenden Sie diesen Befehl:
npm install @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-react-jsx --save-dev
Erstellen Sie eine .babelrc
-Datei:
{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-transform-react-jsx",
{
"pragma": "m",
"pragmaFrag": "'['"
}
]
]
}
Richten Sie ein npm-Skript ein, um Babel auszuführen. Öffnen Sie package.json
und fügen Sie diesen Eintrag unter "scripts"
hinzu:
{
"name": "my-project",
"scripts": {
"babel": "babel src --out-dir bin --source-maps"
}
}
Sie können Babel jetzt mit diesem Befehl ausführen:
npm run babel
Verwenden von Babel mit Webpack
Wenn Sie Webpack noch nicht als Bundler installiert haben, verwenden Sie diesen Befehl:
npm install webpack webpack-cli --save-dev
Sie können Babel in Webpack integrieren, indem Sie diese Schritte ausführen.
npm install @babel/core babel-loader @babel/preset-env @babel/plugin-transform-react-jsx --save-dev
Erstellen Sie eine .babelrc
-Datei:
{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-transform-react-jsx",
{
"pragma": "m",
"pragmaFrag": "'['"
}
]
]
}
Als Nächstes erstellen Sie eine Datei namens webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, './bin'),
filename: 'app.js',
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /\/node_modules\//,
use: {
loader: 'babel-loader',
},
},
],
},
resolve: {
extensions: ['.js', '.jsx'],
},
};
Erfahrene Webpack-Nutzer sollten beachten: Die Babel-Optionen dürfen nicht direkt im babel-loader
-Abschnitt der webpack.config.js
angegeben werden, da dies zu einem Fehler führen kann. Stattdessen müssen sie in der separaten .babelrc
-Datei konfiguriert werden.
Diese Konfiguration setzt voraus, dass sich die Quellcodedatei für den Anwendungseinstiegspunkt in src/index.js
befindet und dass das Bundle in bin/app.js
ausgegeben wird.
Richten Sie ein npm-Skript ein, um den Bundler auszuführen. Öffnen Sie package.json
und fügen Sie diesen Eintrag unter "scripts"
hinzu:
{
"name": "my-project",
"scripts": {
"start": "webpack --mode development --watch"
}
}
Sie können den Bundler jetzt ausführen, indem Sie diesen Befehl in der Befehlszeile ausführen:
npm start
Produktions-Build
Um eine minimierte Datei zu generieren, öffnen Sie package.json
und fügen Sie ein neues npm-Skript mit dem Namen build
hinzu:
{
"name": "my-project",
"scripts": {
"start": "webpack -d --watch",
"build": "webpack -p"
}
}
Sie können Hooks in Ihrer Produktionsumgebung verwenden, um das Produktions-Build-Skript automatisch auszuführen. Hier ist ein Beispiel für Heroku:
{
"name": "my-project",
"scripts": {
"start": "webpack -d --watch",
"build": "webpack -p",
"heroku-postbuild": "webpack -p"
}
}
m
global zugänglich machen
Um global von Ihrem gesamten Projekt aus auf m
zuzugreifen, importieren Sie zunächst webpack
in Ihre webpack.config.js
wie folgt:
const webpack = require('webpack');
Erstellen Sie dann ein neues Plugin in der plugins
-Eigenschaft des Webpack-Konfigurationsobjekts:
{
plugins: [
new webpack.ProvidePlugin({
m: 'mithril',
}),
];
}
Weitere Informationen zu ProvidePlugin
finden Sie in der Webpack-Dokumentation.
Unterschiede zu React
JSX in Mithril weist einige subtile, aber wichtige Unterschiede im Vergleich zu Reacts JSX auf.
Fallkonventionen für Attribut- und Stileigenschaften
React erfordert die Verwendung von Camel-Case für DOM-Eigenschaftsnamen anstelle von HTML-Attributnamen, außer bei data-*
und aria-*
-Attributen. Verwenden Sie beispielsweise className
anstelle von class
und htmlFor
anstelle von for
. In Mithril ist es üblicher, stattdessen die HTML-Attributnamen in Kleinbuchstaben zu verwenden. Mithril greift immer auf das Setzen von Attributen zurück, wenn eine Eigenschaft nicht vorhanden ist, was intuitiver ist mit HTML. Beachten Sie, dass in den meisten Fällen die DOM-Eigenschafts- und HTML-Attributnamen entweder gleich oder sehr ähnlich sind. Zum Beispiel value
/checked
für Eingaben und das globale tabindex
-Attribut im Vergleich zur elem.tabIndex
-Eigenschaft für HTML-Elemente. Nur sehr selten unterscheiden sie sich über den Fall hinaus: Die elem.className
-Eigenschaft für das class
-Attribut oder die elem.htmlFor
-Eigenschaft für das for
-Attribut gehören zu den wenigen Ausnahmen.
In ähnlicher Weise verwendet React immer die in Camel-Case geschriebenen Stileigenschaftsnamen, die im DOM über Eigenschaften von elem.style
verfügbar gemacht werden (wie cssHeight
und backgroundColor
). Mithril unterstützt sowohl dies als auch die in Kebab-Case geschriebenen CSS-Eigenschaftsnamen (wie height
und background-color
) und bevorzugt idiomatischerweise letztere. Nur cssHeight
, cssFloat
und herstellerspezifische Eigenschaften unterscheiden sich in mehr als nur der Groß-/Kleinschreibung.
DOM-Ereignisse
React schreibt den ersten Buchstaben aller Event-Handler groß: onClick
reagiert auf click
-Ereignisse und onSubmit
auf submit
-Ereignisse. Einige werden weiter geändert, da es sich um mehrere Wörter handelt, die miteinander verkettet sind. Zum Beispiel reagiert onMouseMove
auf mousemove
-Ereignisse. Mithril verwendet diese Case-Zuordnung nicht. Stattdessen wird dem nativen Ereignis einfach on
vorangestellt. Um beispielsweise auf click
- und mousemove
-Ereignisse zu reagieren, würden Sie Listener für onclick
bzw. onmousemove
hinzufügen. Dies entspricht viel enger dem HTML-Namensschema und ist viel intuitiver, wenn Sie aus einem HTML- oder Vanilla-DOM-Hintergrund kommen.
React unterstützt das Planen von Event-Listener während der Capture-Phase (im ersten Durchgang, von außen nach innen, im Gegensatz zur standardmäßigen Bubble-Phase, die im zweiten Durchgang von innen nach außen verläuft), indem Capture
an dieses Ereignis angehängt wird. Mithril verfügt derzeit nicht über eine solche Funktionalität, könnte diese aber in Zukunft erhalten. Wenn dies erforderlich ist, können Sie Ihre eigenen Listener manuell in Lifecycle-Hooks hinzufügen und entfernen.
JSX vs. Hyperscript
JSX und Hyperscript sind zwei verschiedene Syntaxen, die Sie zum Definieren von VNodes verwenden können, und sie haben unterschiedliche Vor- und Nachteile:
JSX ist viel zugänglicher, wenn Sie aus einem HTML/XML-Hintergrund kommen und sich wohler fühlen, DOM-Elemente mit dieser Art von Syntax zu definieren. Es ist auch in vielen Fällen etwas übersichtlicher, da es weniger Satzzeichen verwendet und die Attribute weniger visuelles Rauschen enthalten, sodass viele Leute es viel einfacher zu lesen finden. Und natürlich bieten viele gängige Editoren Autocomplete-Unterstützung für DOM-Elemente auf die gleiche Weise wie für HTML. Es erfordert jedoch einen zusätzlichen Build-Schritt, um es zu verwenden, die Editorunterstützung ist nicht so breit wie bei normalem JS und es ist erheblich ausführlicher. Es ist auch etwas ausführlicher, wenn man mit vielen dynamischen Inhalten zu tun hat, da man für alles Interpolationen verwenden muss.
Hyperscript ist zugänglicher, wenn Sie aus einem Backend-JS-Hintergrund kommen, der nicht viel HTML oder XML umfasst. Es ist knapper und enthält weniger Redundanzen und bietet CSS-ähnliche Vereinfachungen für statische Klassen, IDs und andere Attribute. Es kann auch ohne Build-Schritt verwendet werden, obwohl Sie einen hinzufügen können, wenn Sie möchten. Und es ist etwas einfacher, mit vielen dynamischen Inhalten zu arbeiten, da Sie nichts interpolieren müssen. Die Kürze macht es jedoch für manche Leute schwieriger zu lesen, insbesondere für diejenigen, die weniger erfahren sind und aus einem Frontend-HTML/CSS/XML-Hintergrund kommen, und ich kenne keine Plugins, die Teile von Hyperscript-Selektoren wie IDs, Klassen und Attribute automatisch vervollständigen.
Sie können sehen, wie sich die Vor- und Nachteile in komplexeren Bäumen auswirken. Betrachten Sie zum Beispiel diesen Hyperscript-Baum, der aus einem realen Projekt von @dead-claudia mit einigen Änderungen zur Klarheit und Lesbarkeit übernommen wurde:
function SummaryView() {
let tag, posts;
function init({ attrs }) {
Model.sendView(attrs.tag != null);
if (attrs.tag != null) {
tag = attrs.tag.toLowerCase();
posts = Model.getTag(tag);
} else {
tag = undefined;
posts = Model.posts;
}
}
function feed(type, href) {
return m('.feed', [
type,
m('a', { href }, m('img.feed-icon[src=./feed-icon-16.gif]')),
]);
}
return {
oninit: init,
// To ensure the tag gets properly diffed on route change.
onbeforeupdate: init,
view: () =>
m('.blog-summary', [
m('p', 'My ramblings about everything'),
m('.feeds', [
feed('Atom', 'blog.atom.xml'),
feed('RSS', 'blog.rss.xml'),
]),
tag != null
? m(TagHeader, { len: posts.length, tag })
: m('.summary-header', [
m('.summary-title', 'Posts, sorted by most recent.'),
m(TagSearch),
]),
m(
'.blog-list',
posts.map(post =>
m(
m.route.Link,
{
class: 'blog-entry',
href: `/posts/${post.url}`,
},
[
m(
'.post-date',
post.date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})
),
m('.post-stub', [
m('.post-title', post.title),
m('.post-preview', post.preview, '...'),
]),
m(TagList, { post, tag }),
]
)
)
),
]),
};
}
Hier ist das genaue Äquivalent des obigen Codes in JSX. Sie können sehen, wie sich die beiden Syntaxen allein in diesem Bit unterscheiden und welche Kompromisse gelten.
function SummaryView() {
let tag, posts;
function init({ attrs }) {
Model.sendView(attrs.tag != null);
if (attrs.tag != null) {
tag = attrs.tag.toLowerCase();
posts = Model.getTag(tag);
} else {
tag = undefined;
posts = Model.posts;
}
}
function feed(type, href) {
return (
<div class="feed">
{type}
<a href={href}>
<img class="feed-icon" src="./feed-icon-16.gif" />
</a>
</div>
);
}
return {
oninit: init,
// To ensure the tag gets properly diffed on route change.
onbeforeupdate: init,
view: () => (
<div class="blog-summary">
<p>My ramblings about everything</p>
<div class="feeds">
{feed('Atom', 'blog.atom.xml')}
{feed('RSS', 'blog.rss.xml')}
</div>
{tag != null ? (
<TagHeader len={posts.length} tag={tag} />
) : (
<div class="summary-header">
<div class="summary-title">Posts, sorted by most recent</div>
<TagSearch />
</div>
)}
<div class="blog-list">
{posts.map(post => (
<m.route.Link class="blog-entry" href={`/posts/${post.url}`}>
<div class="post-date">
{post.date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</div>
<div class="post-stub">
<div class="post-title">{post.title}</div>
<div class="post-preview">{post.preview}...</div>
</div>
<TagList post={post} tag={tag} />
</m.route.Link>
))}
</div>
</div>
),
};
}
Tipps und Tricks
Konvertieren von HTML in JSX
In Mithril.js ist wohlgeformtes HTML im Allgemeinen gültiges JSX. Oft genügt es, den reinen HTML-Code einzufügen, damit alles funktioniert. Das Einzige, was Sie normalerweise tun müssten, ist, nicht in Anführungszeichen stehende Eigenschaftswerte wie attr=value
in attr="value"
zu ändern und selbstschließende Elemente wie <input>
in <input />
zu ändern, da JSX auf XML und nicht auf HTML basiert.
Bei der Verwendung von Hyperscript müssen Sie HTML oft in Hyperscript-Syntax übersetzen, um es zu verwenden. Um diesen Prozess zu beschleunigen, können Sie einen von der Community entwickelten HTML-zu-Mithril-Template-Konverter verwenden, um einen Großteil davon für Sie zu erledigen.