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 -y
Ha 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-dev
Hozzon 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 babel
A 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-dev
A 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-dev
Hozzon 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.