Render Fonksiyonları & JSX
Render Fonksiyonları & JSX
Vue, uygulamaları oluşturmak için çoğu durumda şablonlar kullanılmasını önerir. Ancak, JavaScript'in tam programatik gücüne ihtiyaç duyduğumuz durumlar da vardır. İşte burada render fonksiyonu kullanabiliriz.
Sanal DOM ve render fonksiyonları kavramına yeniyseniz, önce
Render Mekanizması
bölümünü okumayı unutmayın.
Temel Kullanım
Vnode Oluşturma
Vue, vnode oluşturmak için h()
fonksiyonu sağlar:
import { h } from 'vue'
const vnode = h(
'div', // tür
{ id: 'foo', class: 'bar' }, // özellikler
[
/* çocuklar */
]
)
h()
hiperskript için kısadır - bu "HTML (hiper metin işaretleme dili) üreten JavaScript" anlamına gelir. Bu isim, birçok sanal DOM uygulaması tarafından paylaşılan geleneklerden gelir. Daha açıklayıcı bir isim createVNode()
olabilir, ancak daha kısa bir isim, bu fonksiyonu render fonksiyonu içinde birçok kez çağırmanız gerektiğinde faydalıdır.
h()
fonksiyonu çok esnek olacak şekilde tasarlanmıştır:
// tür dışındaki tüm argümanlar isteğe bağlıdır
h('div')
h('div', { id: 'foo' })
// hem nitelikler hem de özellikler, özellikler içinde kullanılabilir
// Vue otomatik olarak bunu atamanın doğru yolunu seçer
h('div', { class: 'bar', innerHTML: 'merhaba' })
// .prop ve .attr gibi özellik değiştiricileri eklenebilir
// sırasıyla `.` ve `^` ön ekleri ile
h('div', { '.name': 'some-name', '^width': '100' })
// sınıf ve stil, şablonlardaki desteklediği aynı nesne / dizi
// değer desteğine sahiptir
h('div', { class: [foo, { bar }], style: { color: 'red' } })
// olay dinleyicileri onXxx olarak geçilmelidir
h('div', { onClick: () => {} })
// çocuklar bir string olabilir
h('div', { id: 'foo' }, 'merhaba')
// özellikler yoksa özellikler atlanabilir
h('div', 'merhaba')
h('div', [h('span', 'merhaba')])
// çocuklar dizisi karışık vnode ve string içerebilir
h('div', ['merhaba', h('span', 'merhaba')])
Oluşan vnode aşağıdaki şekle sahiptir:
const vnode = h('div', { id: 'foo' }, [])
vnode.type // 'div'
vnode.props // { id: 'foo' }
vnode.children // []
vnode.key // null
Tam VNode
arayüzü birçok başka iç özellik içerir, ancak burada listelenenlerden başka herhangi bir özelliğe güvenilmemesi şiddetle önerilir. Bu, iç özellikler değişirse istenmeyen kırılmaları önler.
Render Fonksiyonlarını Tanımlama
Composition API ile şablonlar kullanıldığında, setup()
kancasının döndürdüğü değer şablona veri açığa çıkarmak için kullanılır. Ancak render fonksiyonları kullanıldığında, render fonksiyonunu doğrudan döndürebiliriz:
import { ref, h } from 'vue'
export default {
props: {
/* ... */
},
setup(props) {
const count = ref(1)
// render fonksiyonunu döndür
return () => h('div', props.msg + count.value)
}
}
Render fonksiyonu setup()
içinde tanımlandığı için doğal olarak aynı kapsamda tanımlanan özelliklere ve reaktif duruma erişimi vardır.
Tek bir vnode döndürmenin yanı sıra, string veya diziler de döndürebilirsiniz:
export default {
setup() {
return () => 'merhaba dünya!'
}
}
import { h } from 'vue'
export default {
setup() {
// birden fazla kök düğüm döndürmek için dizi kullanın
return () => [
h('div'),
h('div'),
h('div')
]
}
}
Doğrudan değerleri döndürmek yerine bir fonksiyon döndürmeyi unutmayın! setup()
fonksiyonu her bileşen için yalnızca bir kez çağrılır, oysa döndürülen render fonksiyonu birden çok kez çağrılacaktır.
Render fonksiyonlarını render
seçeneği kullanarak tanımlayabiliriz:
import { h } from 'vue'
export default {
data() {
return {
msg: 'merhaba'
}
},
render() {
return h('div', this.msg)
}
}
render()
fonksiyonu, this
aracılığıyla bileşen örneğine erişime sahiptir.
Tek bir vnode döndürmenin yanı sıra, string veya diziler de döndürebilirsiniz:
export default {
render() {
return 'merhaba dünya!'
}
}
import { h } from 'vue'
export default {
render() {
// birden fazla kök düğüm döndürmek için dizi kullanın
return [
h('div'),
h('div'),
h('div')
]
}
}
Bir render fonksiyonu bileşeni, herhangi bir durum durumu gerekmediğinde, kısalık için doğrudan bir fonksiyon olarak da tanımlanabilir:
function Hello() {
return 'merhaba dünya!'
}
Evet, bu geçerli bir Vue bileşenidir! Bu sözdizimi hakkında daha fazla ayrıntı için Fonksiyonel Bileşenler
bölümüne bakın.
Vnode'lar Eşsiz Olmalıdır
Bileşen ağacındaki tüm vnode'ların eşsiz olması gerekmektedir. Yani, aşağıdaki render fonksiyonu geçerli değildir:
function render() {
const p = h('p', 'merhaba')
return h('div', [
// Aman Tanrım - kopya vnode'lar!
p,
p
])
}
Aynı öğe/bileşeni birden çok kez kopyalamak istiyorsanız, bir fabrika fonksiyonu ile bunu yapabilirsiniz. Örneğin, aşağıdaki render fonksiyonu, 20 aynı paragrafı oluşturmanın mükemmel geçerli bir yoludur:
function render() {
return h(
'div',
Array.from({ length: 20 }).map(() => {
return h('p', 'merhaba')
})
)
}
JSX / TSX
JSX JavaScript'e benzer bir XML uzantısıdır ve şu şekilde yazmamıza olanak tanır:
const vnode = <div>merhaba</div>
JSX ifadeleri içinde dinamik değerleri gömmek için süslü parantezler kullanın:
const vnode = <div id={dynamicId}>merhaba, {userName}</div>
create-vue
ve Vue CLI, önceden yapılandırılmış JSX desteği ile projeleri oluşturma seçeneklerine sahiptir. Eğer JSX'yi manuel olarak yapılandırıyorsanız, lütfen @vue/babel-plugin-jsx
dokümantasyonuna başvurun.
İlk olarak React tarafından tanıtılmış olmasına rağmen, JSX aslında tanımlı bir çalışma zamanı anlamsalına sahip değildir ve çeşitli çıktılara derlenebilir. Eğer daha önce JSX ile çalıştıysanız, Vue JSX dönüşümünün React'ın JSX dönüşümünden farklı olduğunu unutmayın, bu nedenle Vue uygulamalarında React'ın JSX dönüşümünü kullanamazsınız. React JSX'den bazı dikkat çekici farklılıklar şunlardır:
class
vefor
gibi HTML niteliklerini özellikler olarak kullanabilirsiniz -className
veyahtmlFor
kullanmanıza gerek yoktur.- Bileşenlere çocuklar geçişi (yani slotlar)
farklı çalışır
.
Vue'nun tür tanımlamaları da TSX kullanımı için tür çıkarımı sağlar. TSX kullanırken, TypeScript'in JSX sentaksını Vue JSX dönüşümünün işleyebilmesi için tsconfig.json
dosyasına "jsx": "preserve"
belirttiğinizden emin olun.
JSX Tür Çıkarımı
Dönüşümde olduğu gibi, Vue'nun JSX'sinin de farklı tür tanımlarına ihtiyacı vardır.
Vue 3.4'ü itibariyle, Vue artık global JSX
alanını otomatik olarak kaydetmiyor. TypeScript'e Vue'nun JSX tür tanımlarını kullanması için aşağıdakileri tsconfig.json
dosyanıza eklediğinizden emin olun:
{
"compilerOptions": {
"jsx": "preserve",
"jsxImportSource": "vue"
// ...
}
}
Ayrıca, bir dosya başına /* @jsxImportSource vue */
yorumu ekleyerek dahil olabilirsiniz.
Global JSX
alanının mevcudiyetine bağlı olan kodunuz varsa, projenizde vue/jsx
'yi açıkça içe aktararak veya referans göstererek 3.4 öncesi global davranışı tam olarak koruyabilirsiniz; bu, global JSX
alanını kaydeder.
Render Fonksiyonu Tarifleri
Aşağıda, şablon özelliklerini eşdeğer render fonksiyonları / JSX olarak uygulamak için bazı yaygın tarifler sağlayacağız.
v-if
Şablon:
<div>
<div v-if="ok">evet</div>
<span v-else>hayır</span>
</div>
Eşdeğer render fonksiyonu / JSX:
h('div', [ok.value ? h('div', 'evet') : h('span', 'hayır')])
<div>{ok.value ? <div>evet</div> : <span>hayır</span>}</div>
h('div', [this.ok ? h('div', 'evet') : h('span', 'hayır')])
<div>{this.ok ? <div>evet</div> : <span>hayır</span>}</div>
v-for
Şablon:
<ul>
<li v-for="{ id, text } in items" :key="id">
{{ text }}
</li>
</ul>
Eşdeğer render fonksiyonu / JSX:
h(
'ul',
// varsayılan `items` bir array değerine sahip bir ref'dir
items.value.map(({ id, text }) => {
return h('li', { key: id }, text)
})
)
<ul>
{items.value.map(({ id, text }) => {
return <li key={id}>{text}</li>
})}
</ul>
h(
'ul',
this.items.map(({ id, text }) => {
return h('li', { key: id }, text)
})
)
<ul>
{this.items.map(({ id, text }) => {
return <li key={id}>{text}</li>
})}
</ul>
v-on
Başında on
ve ardından büyük bir harf olan isimlere sahip nitelikler, olay dinleyicisi olarak değerlendirilir. Örneğin, onClick
şablonlarda @click
ile eşdeğerdir.
h(
'button',
{
onClick(event) {
/* ... */
}
},
'Bana Tıkla'
)
<button
onClick={(event) => {
/* ... */
}}
>
Bana Tıkla
</button>
Olay Değiştiricileri
.passive
, .capture
ve .once
olay değiştiricileri, olay isminin üzerine camelCase ile eklenebilir.
Örnek:
h('input', {
onClickCapture() {
/* capture modunda dinleyici */
},
onKeyupOnce() {
/* sadece bir kez tetiklenir */
},
onMouseoverOnceCapture() {
/* bir kez + capture */
}
})
<input
onClickCapture={() => {}}
onKeyupOnce={() => {}}
onMouseoverOnceCapture={() => {}}
/>
Diğer olay ve anahtar değiştiricileri için withModifiers
yardımcı programı kullanılabilir:
import { withModifiers } from 'vue'
h('div', {
onClick: withModifiers(() => {}, ['self'])
})
<div onClick={withModifiers(() => {}, ['self'])} />
Bileşenler
Bir bileşen için bir vnode oluşturmak için h()
'ye geçirilen ilk argüman bileşen tanımı olmalıdır. Yani render fonksiyonlarını kullanırken, bileşeni kaydetmek gerekli değildir - yalnızca içe aktarılan bileşenleri doğrudan kullanabilirsiniz:
import Foo from './Foo.vue'
import Bar from './Bar.jsx'
function render() {
return h('div', [h(Foo), h(Bar)])
}
function render() {
return (
<div>
<Foo />
<Bar />
</div>
)
}
Gördüğümüz gibi, h
, geçerli bir Vue bileşeni olduğu sürece her dosya formatından içe aktarılan bileşenlerle çalışabilir.
Dinamik bileşenler render fonksiyonları ile basittir:
import Foo from './Foo.vue'
import Bar from './Bar.jsx'
function render() {
return ok.value ? h(Foo) : h(Bar)
}
function render() {
return ok.value ? <Foo /> : <Bar />
}
Bir bileşen adıyla kaydedilmiş ve doğrudan içe aktarılamıyorsa (örneğin, bir kütüphane tarafından global olarak kaydedilmişse), resolveComponent()
yardımcı programını kullanarak programlı bir şekilde çözülebilir.
Slotları Render Etme
Render fonksiyonlarında, slotlar setup()
bağlamından erişilebilir. slots
nesnesindeki her slot, bir dizi vnode döndüren bir işlevdir:
export default {
props: ['message'],
setup(props, { slots }) {
return () => [
// varsayılan slot:
// <div><slot /></div>
h('div', slots.default()),
// adlandırılmış slot:
// <div><slot name="footer" :text="message" /></div>
h(
'div',
slots.footer({
text: props.message
})
)
]
}
}
JSX eşdeğeri:
// varsayılan
<div>{slots.default()}</div>
// adlandırılmış
<div>{slots.footer({ text: props.message })}</div>
Render fonksiyonlarında, slotlar this.$slots
üzerinden erişilebilir:
export default {
props: ['message'],
render() {
return [
// <div><slot /></div>
h('div', this.$slots.default()),
// <div><slot name="footer" :text="message" /></div>
h(
'div',
this.$slots.footer({
text: this.message
})
)
]
}
}
JSX eşdeğeri:
// <div><slot /></div>
<div>{this.$slots.default()}</div>
// <div><slot name="footer" :text="message" /></div>
<div>{this.$slots.footer({ text: this.message })}</div>
Slotları Geçirme
Bileşenlere çocukları geçmek, öğelere çocukları geçmekten biraz farklı çalışır. Bir dizi yerine, ya bir slot işlevi ya da bir slot işlevleri nesnesi geçmemiz gerekir. Slot işlevleri, çocuk bileşeninde erişildiğinde her zaman dizi vnode'lara normalleştirilmiş olan herhangi bir şeyi döndürebilir.
// tek varsayılan slot
h(MyComponent, () => 'merhaba')
// adlandırılmış slotlar
// `null`'ın slotların nitelikler olarak değerlendirilmemesi için gerekli olduğunu unutmayın
h(MyComponent, null, {
default: () => 'varsayılan slot',
foo: () => h('div', 'foo'),
bar: () => [h('span', 'bir'), h('span', 'iki')]
})
JSX eşdeğeri:
// varsayılan
<MyComponent>{() => 'merhaba'}</MyComponent>
// adlandırılmış
<MyComponent>{{
default: () => 'varsayılan slot',
foo: () => <div>foo</div>,
bar: () => [<span>bir</span>, <span>iki</span>]
}}</MyComponent>
Slotları işlevler olarak geçirmek, çocuk bileşenin slotların bağımlılıklarını takip etmesine olanak tanır. Bu, slotun bağımlılıklarının ebeveyn yerine çocuk tarafından izlenmesiyle sonuçlanır ve daha doğru ve etkili güncellemeler sağlanır.
Scoped Slotlar
Ebeveyn bileşendeki bir scoped slotu render etmek için, bir slot çocuğa geçilir. Slotta artık text
adıyla bir parametre olduğunu unutmayın. Slot, çocuk bileşende çağrılacak ve çocuk bileşenden gelen veri ebeveyn bileşene aktarılacaktır.
// ebeveyn bileşen
export default {
setup() {
return () => h(MyComp, null, {
default: ({ text }) => h('p', text)
})
}
}
Slotların nitelikler olarak değerlendirilmemesi için null
geçmeyi unutmayın.
// çocuk bileşen
export default {
setup(props, { slots }) {
const text = ref('merhaba')
return () => h('div', null, slots.default({ text: text.value }))
}
}
JSX eşdeğeri:
<MyComponent>{{
default: ({ text }) => <p>{ text }</p>
}}</MyComponent>
Yerleşik Bileşenler
Yerleşik bileşenler
gibi ,
, ,
ve `` render fonksiyonlarında kullanılmak üzere ithal edilmelidir:
import { h, KeepAlive, Teleport, Transition, TransitionGroup } from 'vue'
export default {
setup () {
return () => h(Transition, { mode: 'out-in' }, /* ... */)
}
}
import { h, KeepAlive, Teleport, Transition, TransitionGroup } from 'vue'
export default {
render () {
return h(Transition, { mode: 'out-in' }, /* ... */)
}
}
v-model
v-model
direktifi, şablon derlemesi sırasında modelValue
ve onUpdate:modelValue
nitelikleri olarak genişletilir - bu nitelikleri kendimiz sağlamamız gerekecek:
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
setup(props, { emit }) {
return () =>
h(SomeComponent, {
modelValue: props.modelValue,
'onUpdate:modelValue': (value) => emit('update:modelValue', value)
})
}
}
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
render() {
return h(SomeComponent, {
modelValue: this.modelValue,
'onUpdate:modelValue': (value) => this.$emit('update:modelValue', value)
})
}
}
Özel Direktifler
Özel direktifler, withDirectives
kullanılarak bir vnode'a uygulanabilir:
import { h, withDirectives } from 'vue'
// bir özel direktif
const pin = {
mounted() { /* ... */ },
updated() { /* ... */ }
}
// <div v-pin:top.animate="200"></div>
const vnode = withDirectives(h('div'), [
[pin, 200, 'top', { animate: true }]
])
Eğer direktif isimle kaydedilmiş ve doğrudan içe aktarılamıyorsa, resolveDirective
yardımcı programı kullanılarak çözülebilir.
Şablon Referansları
Composition API ile, şablon referansları doğrudan vnode'a geçilen ref()
ile oluşturulur:
import { h, ref } from 'vue'
export default {
setup() {
const divEl = ref()
// <div ref="divEl">
return () => h('div', { ref: divEl })
}
}
Options API ile, şablon referansları vnode niteliklerinde referans adını string olarak geçirerek oluşturulur:
export default {
render() {
// <div ref="divEl">
return h('div', { ref: 'divEl' })
}
}
Fonksiyonel Bileşenler
Fonksiyonel bileşenler, kendi durumları olmayan alternatif bir bileşen türüdür. Onlar saf fonksiyonlar gibi çalışır: giren prop, çıkan vnode. Bileşen örneği (yani this
yok) oluşturulmadan render edilir ve normal bileşen yaşam döngüsü kancaları olmadan.
Bir fonksiyonel bileşen oluşturmak için, bir seçenek nesnesi yerine düz bir fonksiyon kullanırız. Fonksiyon, bileşenin render
fonksiyonu gibi işlev görür.
Fonksiyonel bir bileşenin imzası setup()
kancası ile aynıdır:
function MyComponent(props, { slots, emit, attrs }) {
// ...
}
Fonksiyonel bileşen için this
referansı yok olduğundan, Vue props
'ı ilk argüman olarak geçirir:
function MyComponent(props, context) {
// ...
}
İkinci argüman, context
, üç özellik içerir: attrs
, emit
ve slots
. Bunlar, örnek özellikleri $attrs
, $emit
ve $slots
ile eşdeğerdir.
Bileşenler için genellikle bulunan yapılandırma seçeneklerinin çoğu, fonksiyonel bileşenler için mevcut değildir. Ancak, props
ve emits
tanımlamak mümkündür, bunları özellikler olarak ekleyerek:
MyComponent.props = ['value']
MyComponent.emits = ['click']
props
seçeneği belirtilmezse, fonksiyona geçirilen props
nesnesi, attrs
ile aynı olacak biçimde tüm nitelikleri içerecektir. Prop isimleri, yalnızca props
seçeneği belirtilirse camelCase'a normalleştirilecektir.
Açık props
'a sahip fonksiyonel bileşenler için, nitelik geçişi
normal bileşenlerle aynı şekilde çalışır. Ancak, açık bir şekilde props
belirtmeyen fonksiyonel bileşenler için, varsayılan olarak yalnızca class
, style
ve onXxx
olay dinleyicileri attrs
'dan miras alınır. Her iki durumda da, nitelik miras alımını devre dışı bırakmak için inheritAttrs
ayarını false
olarak ayarlayabilirsiniz:
MyComponent.inheritAttrs = false
Fonksiyonel bileşenler, normal bileşenler gibi kaydedilebilir ve tüketilebilir. Eğer h()
'ye ilk argüman olarak bir fonksiyon geçirirseniz, bu fonksiyon fonksiyonel bileşen olarak değerlendirilir.