JSX
Leírás
A JSX egy szintaktikai kiterjesztés, amely lehetővé teszi HTML elemek JavaScript kóddal való keverését. Nem része a JavaScript szabványnak, és nem is szükséges alkalmazások fejlesztéséhez, de használata kellemesebb lehet az Ön vagy csapata számára, a preferenciáktól függően.
function MyComponent() {
return {
view: () => m('main', [m('h1', 'Hello world')]),
};
}
// így is írható:
function MyComponent() {
return {
view: () => (
<main>
<h1>Hello world</h1>
</main>
),
};
}JSX használatakor JavaScript kifejezéseket lehet beilleszteni a JSX elemekbe kapcsos zárójelek használatával:
var greeting = 'Hello';
var url = 'https://google.com';
var link = <a href={url}>{greeting}!</a>;
// eredménye: <a href="https://google.com">Hello!</a>.A komponensek használhatók, ha a komponens nevét nagy kezdőbetűvel írjuk, vagy tulajdonságként hivatkozunk rá:
m.render(document.body, <MyComponent />)
// egyenértékű ezzel: m.render(document.body, m(MyComponent))
<m.route.Link href="/home">Go home</m.route.Link>
// egyenértékű ezzel: m(m.route.Link, {href: "/home"}, "Go home")Beállítás
A JSX használatának legegyszerűbb módja egy Babel plugin használata.
A Babel használatához npm szükséges, ami automatikusan települ a Node.js telepítésekor. Az npm telepítését követően hozz létre egy projektkönyvtárat, és futtasd a következő parancsot:
npm init -yHa a Webpacket és a Babelt együtt szeretné használni, ugorjon az alábbi szakaszra.
A Babel önálló eszközként való telepítéséhez használja ezt a parancsot:
npm install @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-react-jsx --save-devHozzon létre egy .babelrc fájlt:
{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-transform-react-jsx",
{
"pragma": "m",
"pragmaFrag": "'['"
}
]
]
}A Babel futtatásához állítson be egy npm scriptet. Nyissa meg a package.json fájlt, és adja hozzá ezt a bejegyzést a "scripts" alá:
{
"name": "my-project",
"scripts": {
"babel": "babel src --out-dir bin --source-maps"
}
}Most már futtathatja a Babelt ezzel a paranccsal:
npm run babelA Babel használata a Webpackkel
Ha még nem telepítetted a Webpack-et csomagkezelőként, futtasd a következő parancsot:
npm install webpack webpack-cli --save-devA Babel integrálása a Webpackbe a következő lépésekkel lehetséges:
npm install @babel/core babel-loader @babel/preset-env @babel/plugin-transform-react-jsx --save-devHozzon létre egy .babelrc fájlt:
{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-transform-react-jsx",
{
"pragma": "m",
"pragmaFrag": "'['"
}
]
]
}Ezután hozzon létre egy webpack.config.js nevű fájlt:
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'],
},
};A Webpack-et már ismerők számára fontos, hogy a Babel beállítások közvetlenül a webpack.config.js fájlban, a babel-loader résznél hibát okoznak, ezért azokat a külön .babelrc fájlban kell megadni.
Ez a konfiguráció azt feltételezi, hogy az alkalmazás belépési pontja a src/index.js fájlban található, és ez a csomagot a bin/app.js fájlba fogja kiírni.
A bundler futtatásához állítson be egy npm scriptet. Nyissa meg a package.json fájlt, és adja hozzá ezt a bejegyzést a "scripts" alá:
{
"name": "my-project",
"scripts": {
"start": "webpack --mode development --watch"
}
}Most már futtathatja a bundlert a parancssorból futtatva:
npm startÉles build
A minifikált fájl generálásához nyissa meg a package.json fájlt, és adjon hozzá egy új build nevű npm scriptet:
{
"name": "my-project",
"scripts": {
"start": "webpack -d --watch",
"build": "webpack -p"
}
}Éles környezetben hook-ok segítségével automatizálható az éles build szkript futtatása. Íme egy példa a Heroku-hoz:
{
"name": "my-project",
"scripts": {
"start": "webpack -d --watch",
"build": "webpack -p",
"heroku-postbuild": "webpack -p"
}
}Az m globális elérhetővé tétele
Ahhoz, hogy az m globálisan elérhető legyen a projektben, először importáld a webpack modult a webpack.config.js fájlba, például így:
const webpack = require('webpack');Ezután hozzon létre egy új plugint a Webpack konfigurációs objektum plugins tulajdonságában:
{
plugins: [
new webpack.ProvidePlugin({
m: 'mithril',
}),
];
}További információkért lásd a Webpack dokumentációját a ProvidePlugin-ről.
Különbségek a Reacthez képest
A Mithril JSX-nek néhány finom, de fontos különbsége van a React JSX-hez képest.
Attribútum és stílus tulajdonság elnevezési konvenciók
A React megköveteli, hogy a data-* és aria-* attribútumok kivételével minden más attribútumnál a camelCase formátumú DOM tulajdonságneveket használd a HTML attribútumnevek helyett. Például a className használata a class helyett és a htmlFor a for helyett. A Mithrilben a kisbetűs HTML attribútumnevek használata az elterjedtebb. A Mithril mindig visszatér az attribútumok beállításához, ha egy tulajdonság nem létezik, ami intuitívabban igazodik a HTML-hez. Vegye figyelembe, hogy a legtöbb esetben a DOM tulajdonság és a HTML attribútumnevek vagy azonosak, vagy nagyon hasonlóak. Például value/checked a bemenetekhez és a tabindex globális attribútum az HTML elemek elem.tabIndex tulajdonságához képest. Nagyon ritkán térnek el a kis- és nagybetűkön túl: az elem.className tulajdonság a class attribútumhoz vagy az elem.htmlFor tulajdonság a for attribútumhoz tartozik a kevés kivétel közé.
Hasonlóképpen, a React mindig a camelCase-es stílus tulajdonságneveket használja, amelyek a DOM-ban az elem.style tulajdonságain keresztül érhetők el (mint például a cssHeight és a backgroundColor). A Mithril támogatja mindezt, mind a kebab-case-es CSS tulajdonságneveket (mint például a height és a background-color), és szokásosabb az utóbbit részesíti előnyben. Csak a cssFloat és a vendor-prefixed tulajdonságok térnek el a kis- és nagybetűkön túl.
DOM események
A React az összes eseménykezelő nevének első betűjét nagybetűsíti: az onClick a click eseményre, az onSubmit pedig a submit eseményre figyel. Néhányat tovább módosítanak, mivel több szót fűznek össze. Például az onMouseMove a mousemove eseményeket figyeli. A Mithril nem alkalmazza ezt a konvenciót, hanem egyszerűen az on előtagot illeszti a natív esemény nevéhez, így a onclick és az onmousemove események figyeléséhez ezeket az eseménykezelőket kell használni. Ez sokkal jobban megfelel a HTML elnevezési sémájának, és természetesebb, ha HTML vagy vanilla DOM háttérből érkezik.
A React támogatja az eseményfigyelők ütemezését a capture fázisban (az első menetben, kívülről befelé, szemben az alapértelmezett bubble fázissal, amely a második menetben befelé halad) azáltal, hogy az eseményhez hozzáfűzi a Capture szót. A Mithril jelenleg nem rendelkezik ilyen funkcionalitással, de a jövőben megszerezheti ezt. Ha ez szükséges, manuálisan hozzáadhatja és eltávolíthatja saját figyelőit az életciklus hookokban.
JSX vs hyperscript
A JSX és a hyperscript két különböző szintaxis a virtuális DOM csomópontok (vnode-ok) létrehozására, és mindkettőnek megvannak a maga előnyei és hátrányai:
A JSX sokkal könnyebben megközelíthető, ha HTML/XML háttérből érkezik, és kényelmesebben ad meg DOM elemeket ilyen szintaxissal. Sok esetben átláthatóbb is, mivel kevesebb írásjelet használ, és az attribútumok kevésbé zsúfoltak, ezért sokan könnyebben olvashatónak találják. És természetesen sok általános szerkesztő automatikus kiegészítést biztosít a DOM elemekhez ugyanúgy, mint a HTML-hez. Használatához azonban extra build lépésre van szükség, a szerkesztői támogatás nem olyan széles, mint a normál JS esetében, és hosszadalmasabb. Egy kicsit hosszadalmasabb is, ha sok dinamikus tartalommal foglalkozik, mert mindent interpolációval kell megoldania.
A Hyperscript könnyebben elsajátítható azok számára, akik backend JavaScript fejlesztői háttérrel rendelkeznek, és kevésbé jártasak a HTML vagy XML technológiákban. Tömörebb, kevesebb redundanciával, és CSS-szerű cukrot biztosít a statikus osztályokhoz, azonosítókhoz és egyéb attribútumokhoz. Akár build lépés nélkül is használható, bár hozzáadhat egyet, ha szeretné. És egy kicsit könnyebb vele dolgozni sok dinamikus tartalom esetén, mert nem kell semmit "interpolálnia". Azonban a tömör szintaxis megnehezítheti az olvasást egyesek számára, különösen azoknak, akik kevésbé jártasak a frontend HTML/CSS/XML technológiákban. Emellett nem ismerek olyan bővítményeket, amelyek automatikusan kiegészítenék a hyperscript szelektoraiban található azonosítókat, osztályokat vagy attribútumokat.
A kompromisszumok összetettebb fákban mutatkoznak meg. Például vegye figyelembe ezt a hyperscript fát, amelyet egy valós projektből adaptált @dead-claudia némi módosítással a tisztaság és az olvashatóság érdekében:
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,
// Annak biztosítására, hogy a tag megfelelően diffelve legyen az útvonal változásakor.
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 }),
]
)
)
),
]),
};
}Itt van a fenti kód pontos megfelelője, JSX használatával. Láthatja, hogy a két szintaxis csak ebben a kicsit különbözik, és milyen kompromisszumok vonatkoznak rájuk.
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,
// Annak biztosítására, hogy a tag megfelelően diffelve legyen az útvonal változásakor.
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>
),
};
}Tippek és trükkök
HTML konvertálása JSX-re
A Mithril.js-ben a helyesen formázott HTML kód általában érvényes JSX kódnak minősül. A nyers HTML kód beillesztésénél nem sokkal több szükséges ahhoz, hogy a dolgok működjenek. Körülbelül az egyetlen dolog, amit általában meg kell tennie, az az, hogy a nem idézőjeles tulajdonságértékeket, mint például az attr=value-t attr="value"-re változtatja, és az üres elemeket, mint például az <input>-ot <input />-re, mivel a JSX XML-en alapul, nem pedig HTML-en.
A hyperscript használatakor gyakran le kell fordítania a HTML-t hyperscript szintaxisra a használatához. A folyamat felgyorsításához használhatsz egy közösség által fejlesztett HTML-to-Mithril-template konvertáló eszközt, amely a konvertálás nagy részét elvégzi helyetted.