JSX
Açıklama
JSX, JavaScript içine HTML etiketleri yazmanızı sağlayan bir sözdizimi uzantısıdır. Herhangi bir JavaScript standardının parçası değildir ve uygulama oluşturmak için zorunlu değildir; ancak sizin veya ekibinizin tercihine bağlı olarak kullanımı daha uygun olabilir.
function MyComponent() {
return {
view: () => m('main', [m('h1', 'Hello world')]),
};
}
// şu şekilde de yazılabilir:
function MyComponent() {
return {
view: () => (
<main>
<h1>Hello world</h1>
</main>
),
};
}
JSX kullanırken, küme parantezleri kullanarak JavaScript ifadelerini JSX etiketleri içine yerleştirebilirsiniz:
var greeting = 'Hello';
var url = 'https://google.com';
var link = <a href={url}>{greeting}!</a>;
// <a href="https://google.com">Hello!</a> çıktısını verir
Bileşenler, bileşen adının ilk harfini büyük yaparak veya bir özellik olarak erişerek kullanılabilir:
m.render(document.body, <MyComponent />)
// m.render(document.body, m(MyComponent)) ile eşdeğerdir
<m.route.Link href="/home">Go home</m.route.Link>
// m(m.route.Link, {href: "/home"}, "Go home") ile eşdeğerdir
Kurulum
JSX kullanmanın en basit yolu bir Babel eklentisi kullanmaktır.
Babel, Node.js yüklediğinizde otomatik olarak yüklenen npm paket yöneticisini gerektirir. Npm yüklendikten sonra, bir proje klasörü oluşturun ve proje dizininde şu komutu çalıştırın:
npm init -y
Webpack ve Babel'i birlikte kullanmak istiyorsanız, aşağıdaki bölüme geçin.
Babel'i bağımsız bir araç olarak yüklemek için şu komutu kullanın:
npm install @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-react-jsx --save-dev
Bir .babelrc
dosyası oluşturun:
{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-transform-react-jsx",
{
"pragma": "m",
"pragmaFrag": "'['"
}
]
]
}
Babel'i çalıştırmak için bir npm betiği ayarlayın. package.json
dosyasını açın ve "scripts"
altında şu girdiyi ekleyin:
{
"name": "my-project",
"scripts": {
"babel": "babel src --out-dir bin --source-maps"
}
}
Artık şu komutu kullanarak Babel'i çalıştırabilirsiniz:
npm run babel
Webpack ile Babel Kullanımı
Webpack'i zaten bir paketleyici olarak yüklemediyseniz, şu komutu kullanın:
npm install webpack webpack-cli --save-dev
Babel'i Webpack'e entegre etmek için şu adımları izleyin:
npm install @babel/core babel-loader @babel/preset-env @babel/plugin-transform-react-jsx --save-dev
Bir .babelrc
dosyası oluşturun:
{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-transform-react-jsx",
{
"pragma": "m",
"pragmaFrag": "'['"
}
]
]
}
Ardından, webpack.config.js
adlı bir dosya oluşturun:
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'],
},
};
Webpack kullanıcıları dikkat: Babel seçeneklerini doğrudan webpack.config.js
dosyasındaki babel-loader
bölümüne eklemek hatalara neden olabilir. Bu nedenle, bu seçenekleri ayrı bir .babelrc
dosyasında tanımlamanız önerilir.
Bu yapılandırma, uygulama giriş noktasının src/index.js
içinde olduğunu ve çıktının bin/app.js
'ye paketleneceğini varsayar.
Paketleyiciyi çalıştırmak için bir npm betiği ayarlayın. package.json
dosyasını açın ve "scripts"
altında şu girdiyi ekleyin:
{
"name": "my-project",
"scripts": {
"start": "webpack --mode development --watch"
}
}
Artık komut satırından şunu çalıştırarak paketleyiciyi çalıştırabilirsiniz:
npm start
Üretim Derlemesi
Minifiye edilmiş bir dosya oluşturmak için package.json
dosyasını açın ve build
adlı yeni bir npm betiği ekleyin:
{
"name": "my-project",
"scripts": {
"start": "webpack -d --watch",
"build": "webpack -p"
}
}
Üretim derleme betiğini otomatik olarak çalıştırmak için üretim ortamınızda kancalar kullanabilirsiniz. İşte Heroku için bir örnek:
{
"name": "my-project",
"scripts": {
"start": "webpack -d --watch",
"build": "webpack -p",
"heroku-postbuild": "webpack -p"
}
}
m
'yi Global Olarak Erişilebilir Hale Getirme
Projenizin tamamından m
'ye global olarak erişmek için, öncelikle webpack.config.js
dosyanıza webpack
'i şu şekilde içe aktarın:
const webpack = require('webpack');
Ardından, Webpack yapılandırma nesnesinin plugins
özelliğinde yeni bir eklenti oluşturun:
{
plugins: [
new webpack.ProvidePlugin({
m: 'mithril',
}),
];
}
ProvidePlugin
hakkında daha fazla bilgi için Webpack belgelerine bakın.
React ile Farklılıklar
Mithril'deki JSX, React'in JSX'ine kıyasla bazı ince ancak önemli farklılıklara sahiptir.
Öznitelik ve Stil Özelliği Büyük/Küçük Harf Kuralları
React, data-*
ve aria-*
öznitelikleri dışındaki tüm öznitelikler için HTML öznitelik adları yerine camel-cased DOM özellik adlarını kullanmanızı gerektirir. Örneğin, class
yerine className
ve for
yerine htmlFor
kullanmak gibi. Mithril'de bunun yerine küçük harfli HTML öznitelik adlarını kullanmak daha yaygındır. Mithril, bir özellik mevcut olmadığında her zaman öznitelikleri ayarlamaya geri döner, bu da HTML ile daha doğal olarak uyumludur. Çoğu durumda, DOM özelliği ve HTML öznitelik adlarının aynı veya çok benzer olduğunu unutmayın. Örneğin, girişler için value
/checked
ve elem.tabIndex
HTML öğelerindeki özelliği ile tabindex
genel özniteliği. Büyük/küçük harf dışında nadiren farklılık gösterirler: class
özniteliği için elem.className
özelliği veya for
özniteliği için elem.htmlFor
özelliği birkaç istisnadır.
Benzer şekilde, React her zaman elem.style
özellikleriyle (örneğin, cssHeight
ve backgroundColor
) DOM'da kullanıma sunulan camel-cased stil özelliği adlarını kullanır. Mithril hem bunu hem de kebab-cased CSS özelliği adlarını (örneğin, height
ve background-color
) destekler ve deyimsel olarak ikincisini tercih eder. Yalnızca cssHeight
, cssFloat
ve satıcı ön ekli özellikler büyük/küçük harften daha fazla farklılık gösterir.
DOM Olayları
React, tüm olay işleyicilerinin ilk karakterini büyük harfe çevirir: onClick
, click
olaylarını ve onSubmit
, submit
olaylarını dinler. Bazıları, birden çok kelimenin bir araya getirilmesiyle daha da değiştirilir. Örneğin, onMouseMove
, mousemove
olaylarını dinler. Mithril bu büyük/küçük harf eşlemesini yapmaz, bunun yerine sadece yerel olaya on
ekler, bu nedenle bu iki olayı dinlemek için onclick
ve onmousemove
için dinleyiciler eklersiniz. Bu, HTML'nin adlandırma şemasına çok daha yakındır ve bir HTML veya vanilla DOM arka planından geliyorsanız çok daha sezgiseldir.
React, yakalama aşamasında (varsayılan kabarcık aşamasının aksine, ilk geçişte dışarıdan içeriye, ikinci geçişte içeriden dışarıya doğru) olay dinleyicilerini zamanlamayı, bu olaya Capture
ekleyerek destekler. Mithril şu anda böyle bir işlevselliğe sahip değil, ancak gelecekte bunu kazanabilir. Bu gerekliyse, yaşam döngüsü kancalarında kendi dinleyicilerinizi manuel olarak ekleyebilir ve kaldırabilirsiniz.
JSX ve Hyperscript
JSX ve hyperscript, vnode'ları belirtmek için kullanabileceğiniz iki farklı sözdizimidir ve farklı avantaj ve dezavantajları vardır:
JSX, bir HTML/XML arka planından geliyorsanız ve bu tür bir sözdizimiyle DOM öğelerini belirtmekten daha rahatsanız çok daha kolay öğrenilebilir. Ayrıca, birçok durumda biraz daha temizdir, çünkü daha az noktalama işareti kullanır ve öznitelikler daha az görsel gürültü içerir, bu nedenle birçok kişi okumayı çok daha kolay bulur. Ve elbette, birçok yaygın düzenleyici, HTML için yaptıkları gibi DOM öğeleri için otomatik tamamlama desteği sağlar. Ancak, kullanmak için ekstra bir derleme adımı gerektirir, düzenleyici desteği normal JS kadar geniş değildir ve dosya boyutu önemli ölçüde daha büyüktür. Ayrıca, her şey için enterpolasyonlar kullanmanız gerektiğinden, çok fazla dinamik içerikle uğraşırken biraz daha ayrıntılıdır.
Hyperscript, çok fazla HTML veya XML içermeyen bir arka uç JS arka planından geliyorsanız daha kolay öğrenilebilir. Daha az yedeklilikle daha kısadır ve statik sınıflar, kimlikler ve diğer öznitelikler için CSS benzeri bir sözdizimi sağlar. Ayrıca, hiç derleme adımı olmadan da kullanılabilir, ancak isterseniz bir tane ekleyebilirsiniz. Ve çok fazla dinamik içerikle uğraşırken çalışmak biraz daha kolaydır, çünkü hiçbir şeyi "enterpole" etmeniz gerekmez. Ancak, bu kısalık, özellikle daha az deneyimli ve bir ön uç HTML/CSS/XML arka planından gelen bazı kişiler için okumayı zorlaştırır ve kimlikler, sınıflar ve öznitelikler gibi hyperscript seçicilerinin bölümlerini otomatik olarak tamamlayan herhangi bir eklenti bilmiyorum.
Avantaj ve dezavantajların daha karmaşık yapılarda nasıl etkili olduğunu görebilirsiniz. Örneğin, @dead-claudia tarafından gerçek dünyadaki bir projeden uyarlanan ve netlik ve okunabilirlik için bazı değişiklikler yapılan bu hyperscript ağacını düşünün:
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,
// Etiketin yönlendirme değişikliğinde doğru şekilde güncellenmesini sağlamak için.
onbeforeupdate: init,
view: () =>
m('.blog-summary', [
m('p', 'Her şey hakkındaki gevezeliklerim'),
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', 'Gönderiler, en son tarihe göre sıralanmış.'),
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 }),
]
)
)
),
]),
};
}
İşte yukarıdaki kodun tam eşdeğeri, bunun yerine JSX kullanılarak. Sadece bu bit içinde iki sözdiziminin nasıl farklılaştığını ve hangi avantaj ve dezavantajların uygulandığını görebilirsiniz.
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,
// Etiketin yönlendirme değişikliğinde doğru şekilde güncellenmesini sağlamak için.
onbeforeupdate: init,
view: () => (
<div class="blog-summary">
<p>Her şey hakkındaki gevezeliklerim</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">Gönderiler, en son tarihe göre sıralanmış</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>
),
};
}
İpuçları ve Püf Noktaları
HTML'yi JSX'e Dönüştürme
Mithril.js'de, iyi biçimlendirilmiş HTML genellikle geçerli bir JSX'dir. Sorunsuz bir geçiş için ham HTML'yi yapıştırmaktan biraz daha fazlası gerekir. Genellikle yapmanız gereken tek şey, attr=value
gibi tırnaksız özellik değerlerini attr="value"
olarak değiştirmek ve <input>
gibi boş öğeleri <input />
olarak değiştirmektir, çünkü JSX, HTML değil XML tabanlıdır.
Hyperscript kullanırken, kullanmak için genellikle HTML'yi hyperscript sözdizimine çevirmeniz gerekir. Bu süreci hızlandırmaya yardımcı olmak için, bunun çoğunu sizin için yapmak üzere topluluk tarafından oluşturulmuş bir HTML-Mithril-şablon dönüştürücü kullanabilirsiniz.