Vue JS — Türkçe kaynak

Yavuz AKINCI
KoçSistem
Published in
53 min readDec 27, 2021

Sol şeridi boşaltın Vue Js geçiyor.

Merhaba,

Geç olsun güç olmasın çatısı altında başladığım ve iki sene sonra bitirme fırsatına eriştiğim Vue Js makalemle karşınızdayım. İki yıl içinde artık herkes tahminimce Vue Js’i yalamış yutmuş, kitap yazmış, hatta imza günleri düzenlediğini düşünsem de hala bir yerlerde Nedir bu Vue Js? Diye merak eden ve Türkçe detaylı bir kaynak arayışında olan insanlara hitaben yine çook uzunn :) el kitabı tadında bir yazı yazmış bulunmaktayım. Ufak bir geçmişe giderek giriş yapalım.

Geçmişten günümüze, bir web sitesi hazırlayacağımız zaman html ve css’i o sitenin yapı taşı olarak hep kullandık ve halen daha kullanıyoruz. İşin içine daha profesyonel web sayfaları yapmak yada kullanıcı deneyimini arttırmak da devreye girdiğinde javascript bu yapı taşlarının yanında hayatımıza karıştı ve vazgeçilmezlerimizden oldu. Teknoloji günden güne ilerledi ve web teknolojilerinde back-end tarafındaki server-side kodlarını browser üzerine taşımak gibi devrim niteliğinde fikirler ortaya çıkmaya başladı. Bu noktada javascript bu etkileşimi sağlamak için kolları sıvadı ve back-end de bulunan kodları developerlar javascript kodları halinde yazmaya başladı. Javascript bu yükü bir şekilde göğüslese de günün sonunda projelerimizde kod kalabalığının ve javascript dosyalarının çok fazla olması gibi oldukça eski ve kötü bir yönteme dönüşmeye başladı.

Bu eksikliği gidermek için developer’lar modern javascript framework’leri ve kütüphaneler geliştirmeye başladılar. Bunların arasında en çok öne çıkan Angular, ardından React ve bunlardan esinlenerek çıkartılan ancak kendi artılarını çok fazla ortaya koyan Vue js geliştirildi.

Peki nedir bu Vue Js ?

  1. Modern bir prograsive javascript framework’üdür.
  2. Kullanıcı dostudur.
  3. Yazım şekli ve özellikleri açısından oldukça anlaşılır kolay bir yapısı vardır.
  4. Çok yönlüdür. İstediğiniz herhangi bir özelliğini kullanabilirsiniz.
  5. React ve Angular’a oranla daha performanslıdır.
  6. Sürdürülebilirdir. Siz bir uygulamayı canlıya çıktığınızda canlıdaki uygulama üzerinden rahatlıkla geliştirmeye devam edebilirsiniz.
  7. Test edilebilirdir. Kendiniz uygulamanıza çeşitli test senaryoları yazabilirsiniz.

Reusable component yapısına sahiptir. Yani kendi içerisinde html,css ve javascriptleri barındıran ufak uygulamalara sahiptir. Örneğin sitenizde bulunan header, product alanı veya footer kısmınızını sadece bir kerelik component olarak tanımlayabilir ve istediğiniz yerde tekrardan kod yazmadan kullanabilirsiniz.

DOM etkileşimleri

Vue js çalışırken kontrol ettiği html bloğunu ele alır ve yorumlar(render). Yorumladığı html bloğundan bir sonuç çıkartır ve ardından bu sonucu DOM’a basar. Yani html üzerinde çalışma anında bir işlem yapmaz.

...
<head>
...
<title>ilk örnek</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="deneme">
<h1>{{title}}</h1>
<p>{{description}}</p>
</div>
<script>
new Vue({
el: "#deneme",
data: {
title: "Başlık",
description: "Açıklama"
}
})
</script>
</body>
</html>

Vue js’nin en güzel yönü direkt değil dolaylı yönden html üzerinde işlem yapıyor olmasıdır. Vue js’nin instance’ı yaratıldığı anda, bizim belirttiğimiz html bloğunu ele alır. Sizin süslü parantezlerle html bloğunun içerisine belirttiğiniz veriyi data içerisinde belirtmiş olduğunuz veri ile kıyaslar. Eğer iki veride birbiri karşılıyorsa sanki server-side’da bir işlem yapılmış da html alanının içine basılmış gibi ilgili yere datayı bind eder. Bu işlem sadece dataya özel olmamakla birlikte aynı şekilde bir fonksiyonun return değerini de html’e bind edebilirsiniz.

...
<body>
<div id="deneme">
<h1>{{title}}</h1>
<p>{{description}}</p>
<p>
{{ merhaba()}}
</p>

</div>
<script>
new Vue({
el: "#deneme",
data: {
...
},
methods : {
merhaba : function(){
return "merhaba vue js";
}
}

})
</script>
</body>
</html>

Örneğin aynı örnekte bulunan vue’nin instance’ı içerisinde string bir yazı return ederek html alanında bu fonksiyonun sonuç değerini sayfaya basabiliriz.

Dikkat ederseniz süslü parantezlerin içerisine hiç bir şekilde javascript ile alakalı bir şey tanımlamıyoruz. Vue instance’ı içerisinde ne çıktı ürettiyse ilgili yere bunu sadece string olarak basıyor.

Instance içerisinde göstereceğiniz property’nin karşılığını eğer bir html tagı olarak yazarsanız, (Ör; title : <div>merhaba</div>) html taglarını geçersiz kılacak ve sanki bir string değer göstermişsiniz gibi ekrana html tagları ile birlikte basacaktır.

...
new Vue({
el: "#yavuz",
data: {
title: "Başlık",
description: "Açıklama"
},
methods : {
merhaba : function(){
return this.title + this.description
}
}
})
...

Eğer Vue Js instance’ı içerisinde bir dataya ulaşmak isterseniz this anahtar kelimesi ile ulaşabilirsiniz. This burada Vue instance’ını temsil eder. Ardından bir property yazdığınızda “vue’nin içinde bulunan title’ı veya descirption’u bana getir” demiş olursunuz.

Directive nedir? Nasıl kullanılır ?

Direktifler Vue Js tarafından işlem yapmamızı sağlayan tanımlamalardır. Süslü parantez kullandığımızda bir veriyi html’e yani view katmanına yazdırabilirsiniz. Yalnız verimiz dinamik olduğunda bunu ekrana yazdırmak için direktiflerden yararlanmamız gerekmektedir. Vue js kendi içerisinde birçok built-in direktif barındırır. İlerleyen konularda bunların çoğunu görecek ve kendimiz de direktifler oluşturacağız. Yalnız şuan için kullanımı ile alakalı bilgi sahibi olmanız için bir iki örnek üzerinden direktifleri inceleyelim.

V-bind
Burada istediğimiz verinin attribute’una erişebilmek için v-bind adında bir direktiften yararlandık. V-bind’ın burada yaptığı işlem Vue Js(v-)‘i kullanarak, href attribute’una(:href)erişiyoruz, ardından title dinamik property’sini href’e bağlıyoruz(bind).

<div id="deneme2">
{{title}} : <br/>
<a target="_blank" v-bind:href=link>Siteye Git</a>
</div>
<script>
new Vue({
el: "#deneme2",
data: {
title: "google",
link : "https://www.google.com.tr/"
}
})
</script>

Kısa yazma tekniği
Bir event bind etmek istiyorsak ve v-bind kullanıyorsak “v-bind:” yerine sadece “:” kullanabiliriz.

Bu örneği tam tersten giderek de yapabiliriz. Yani link kısmında html gönderip sayfaya yine basabiliriz.

V-html
Daha önce hatırlarsanız süslü parantezler ile bind olduğumuz da Vue js’nin string bastığını ve html tag kullanırsanız etkisiz olacağını belirtmiştik. Bu durumdan kurulmamız için v-html adında bir direktif kullanmamız gerekir.

<div id="deneme2">
{{title}} : <br/>
<p v-html=link>
{{link}}
</p>
</div>
<script>
new Vue({
el: "#deneme2",
data: {
title: "google",
link : "<a target='_blank' href='https://www.google.com.tr/'>Siteye Git</a>"
}
})
</script>

V-once
Başka bir örnek verecek olursak; Örneğin re-render işlemini engellemek istiyorsunuz. Yani daha önce tanımladığınız bir datayı instance’ın içerisinde farklı şekilde tanımladınız. Ancak bir önce tanımladığınız property’nizin karşılığının bozulmamasını istiyorsunuz. Bunun için v-once direktifinden yararlanmanız gerekmektedir.

<div id="deneme2">
<div v-once>
{{title}} <b>v-once</b> kullanıldı.
</div>
{{merhaba()}}
</div>
<script>
new Vue({
el: "#deneme2",
data: {
title: "google"
},
methods : {
merhaba : function () {
this.title = "Merhaba"
return this.title;
}
}
})
</script>

Böyle bir yapıda eğer v-once direktifinden yararlanmazsanız karşılaşacağınız çıktı sayfada iki tane alt alta “Merhaba” yazısı olacaktır. V-once ilk render’dan sonra html üzerinde verinin data içerisinde neyse öyle kalmasını sağlayan bir direktiftir.

Yani datanın içinde bulunan title’ı sayfaya bind ederken v-once kullanırsanız title’ın sonradan değiştirilmesini görmezden gelerek olduğu gibi kalmasını sağlarsınız.

V-on
Bu direktif event binding işlemleri için kullanılır. Örneğin bir textbox’a yazı yazdığınızda hedef aldığınız bir html alına yazdığınız yazıları bind etmek istediğimizde, click event’i ile fonksiyonları tetiklemek yada javascript’in diğer eventlerinden yararlanarak tüm bind işlemlerinizde v-on’u kullanabilirsiniz. Vue Js ile event binding işlemlerini örnekler üzerinden inceleyelim.

Event’i bind etmek

<div id="deneme2">
<p>İsim : {{name}}</p>
<input type="text" v-on:input="changeName">
</div>
<script>
new Vue({
el: "#deneme2",
data: {
name : "yavuz",
},
methods:{
changeName : function(e){
this.name = e.target.value;
}
}
})
</script>

Click event ile function tetiklemek

<body>
<div id="odev1">
<p>İsim : {{name}}</p>
<button v-on:click="changeName">Change Name</button>
</div>
<script>
new Vue({
el: "#odev1",
data: {
name : "yavuz",
},
methods:{
changeName : function(){
this.name = "Akıncı";
}

}
})
</script>

Event ile parametreyi birlikte göndermek

<div id="deneme3">
<p>number : {{number}}</p>
//Adım 1
<p v-on:mousemove="showMouseCoordinate(10, $event)">
Koordinat : {{x}} - {{y}}
// Adım 3
<span v-on:mousemove.stop style="background:#e1e1e1; padding:10px;">Bind işlemini durdurma alanı</span>
</p>
</div>
<script>
new Vue({
el: "#deneme3",
data: {
number: 0,
x: 0,
y: 0
},
methods: {
//Adım 2
showMouseCoordinate: function (onarArttir, event) {
this.number += onarArttir;
this.x = event.clientX;
this.y = event.clientY;
}
}
})
</script>

Bu örneği biraz detaylı inceleyelim. Burada mouse’umuzu hareket ettirdiğimizde hem koordinatını almak hem de numaramızın değerini on ekleyerek arttırmak istiyoruz.
1: Normalde bir parametre ve event’i birlikte göndereceğiniz zaman event’i kaybettiğinizi düşünebilirisiniz. Ancak bu durumda event’iniz kaybolmuyor. Paremetre ile birlikte göndereceğiniz zaman sadece event’i hangi sırada göndereceğinizi belirlemek ve başına dolar($) işareti koymanız yeterli oluyor.
2: Burada showMouseCoordinate fonksiyonumuza gönderdiğimiz parametre ve event sayesinde mouse’umuzu hareket ettirdikçe hem x,y koordinatlarını yazdırıyoruz hemde numaramızın değerini on ekleyerek arttırıyoruz.
3: Bir eventin kendine has özelliklerini yapmamızı engelleyen özelliğe modifier denir. Bu işlem jquery ve javascript’den de uzak olmadığımız bir kavram. Yalnız Vue Js’de modifier işlemlerini yapmak biraz farklı, ancak daha kolay. Burada mousemove işleminin olduğu html taglarının arasına bu işlemi durduracak bir “stop” html’i oluşturarak modifier işlemi yapabilirsiniz.

Modifier işlemlerinin tamamı için bu linkten yararlanabilirsiniz.

Klavye modifier eventleri

Jquery ve javascript’deki gibi enter veya space’e basıldığı zaman yada zincirleme olarak ikisine de basıldığında fonksiyonu tetikleyebiliriz.

Klavye modifier işlemlerinin tamamı için bu linkten yararlanabilirsiniz. Bu linke gittiğinizde sol menüde bulunan tüm elementlerin modifier’lerine ulaşabilirsiniz. ( Form Input modifier, Event Handling modifier vb.)

<div id="deneme4">
<input type="text" v-on:keyup.enter.space="klavyeKullanimi">
</div>
<script>
new Vue({
el: "#deneme4",
methods: {
klavyeKullanimi: function (e) {
console.log(e.target.value)
}
}
})
</script>

Kısa yazma tekniği
Bir event’i click ile bind etmek istiyorsak ve v-on kullanıyorsak “v-on:” yerine “@” kullanabiliriz.

Örneklere biraz ara verelim ve konu anlatımına devam edelim. Diğer direktiflere ilerleyen konularda tekrar inceleyeceğiz. Eğer sabırsızlanıyorsanız ve tüm direktif listesini görmek isterseniz buradan bakabilirsiniz.

Two-way data binding

Şu ana kadar instance’da bulunan veriyi template üzerinde göstermek için attribute binding yaptık, süslü parantezlerle string olarak bastık yada metot tanımlayıp üzerinden değeri return ettik. Ancak bu işlemleri gerçekleştirirken hep ya bir event’i tetikleyerek yada event’in value’sini dinleyerek gerçekleştirdik. Aslında two way data binding işlemi bize Angular veya React’den de yabancı olmadığımız veriyi hem okuma hemde aynı zamanda yazma işlemlerini yapmamızı sağlayan bir veri bağlama özelliği.
Peki Vue js de bu özelliği nasıl kullanıyoruz bir örnek üzerinden inceleyelim;

<div id="deneme5">
<input type="text" v-model="title">
<p>{{title}}</p>
<span>{{title}}</span>
<h3>{{title}}</h3>
</div>
<script>
new Vue({
el: "#deneme5",
data: {
title: "Başlık"
}
})
</script>

V-model
Karşısında instance bulunan bir veriyi alır. Input’un etki etmesini istediğiniz value neyse v-model’in karşısına onu yazmanız gerekir.

Computed property ile reactivity çalışmak

Reactivity’yi zincirleme bir reaksiyon olarak düşünebilirsiniz. Örneğin bir butona tıkladığımızda data içerisindeki bir property’nin değerinin değişmesi, aynı zamanda değişen propery’ye bağlı başka bir property’nin de bundan etkilenmesi olarak düşünebilirsiniz.

<div id="deneme6">
<button v-on:click="sayi++">Arttır</button>
<button v-on:click="sayi--">Azalt</button>
<p>sayi : {{sayi}}</p>
<p>{{ sonuc() }}</p>
</div>
<script>
new Vue({
el: "#deneme6",
data: {
sayi: 0,
},
methods: {
sonuc: function () {
return this.sayi > 10 ? "10 dan büyük" : "10 dan kucuk";
}
}
})
</script>

Diyelim ki yukarıdaki gibi butonlara basılınca sayıyı bir arttıran veya azaltan bir uygulamamız olsun. İlk anda baktığımızda hiç bir sıkıntısı olmayan ve gayet düzgün çalışan bu kodun derinine indiğimizde aslında çok büyük sıkıntıları olduğunu göreceksiniz. Data içerisinde bulunan herhangi bir property’nin değeri değişiyorsa ve bu değişen property’nin değerini template üzerinde yazdırıyorsak bu property’nin değeri değiştikçe template kendini yeniden yükleyecektir(render). Bu durum yukarıdaki kodda çok fark edilmese de, daha büyük datalarla uğraştığımızda bu render işlemi her seferinde gerçekleşecek ve bizim zaman maliyetimizi çok fazla alacaktır. Hatta yukarıdaki alanda başka bir data property’si de kullanırsak her butona bastığımızda alakası olmasa bile o property de render edilecektir.

<div id="deneme6">
<button v-on:click="sayi++">Arttır</button>
<button v-on:click="sayi--">Arttır</button>
<button v-on:click="sayi2++">sayi2 Render denemesi</button>
<p>sayi : {{sayi}} | {{sayi2}}</p>
<p>{{ sonuc() }}</p>
</div>
<script>
new Vue({
el: "#deneme6",
data: {
sayi: 0,
sayi2: 0
},
methods: {
sonuc: function () {
console.log("metot çalıştı...")
return this.sayi > 10 ? "10 dan büyük" : "10 dan kucuk";
}
}
})
</script>

Yukarıdaki örneğe ek olarak ikinci bir sayı ekleyelim. Ayrıca bu sayi2'yi arttıracak bir buton tanımlayalım.

Html’imizde iki defa en solda bulunan sayi’yi arttır’a ve bir defa da en sağda bulunan sayi2'yi arttırma butonuna bastık. Dikkat ederseniz sayi2'nin metodumuzla hiç bir alakası olmamasına rağmen sayi2 butonuna basılınca console’a tekrardan “metot çalıştı…” yazdı. İşte problemin kaynağı burası. Sayi2 gibi yüzlerce property’nin olabileceğini düşündüğünüzde karşımıza korkunç bir rakam çıkıyor.
Bu sorunu çözmemiz için kullanacağımız adres computed property’dir. Çünkü, computed property datadaki herhangi bir property gibi çalışır ancak metot gibi çalışan bir objedir. Method ve computed arasında çok temel bir fark vardır. Computed; kodu analiz eder ve o kodun içerisinde ihtiyacı olan değişken eğer değişmişse çalışır ve tekrardan çalıştırılmaz. Yani sadece bizim data objemiz içerisindeki property’ye bağımlılığı ile çalışır ve computed içerisinde tanımlamış olduğumuz ifadeler etkilenmez.

<div id="deneme6">
<button v-on:click="sayi++">Arttır</button>
<button v-on:click="sayi--">Arttır</button>
<button v-on:click="sayi2++">sayi2 Render denemesi</button>
<p>sayi : {{sayi}} | {{sayi2}}</p>
<p>{{sonuc}}</p>
</div>
<script>
new Vue({
el: "#deneme6",
data: {
sayi: 0,
sayi2: 0
},
methods: {
// sonuc: function () {
// console.log("metot çalıştı...")
// return this.sayi > 10 ? "10 dan büyük" : "10 dan kucuk";
// }

},
computed : {
sonuc : function(){
console.log("computed çalıştı...")
return this.sayi > 10 ? "10 dan büyük" : "10 dan kucuk";
}

}
})
</script>

Dikkat ederseniz “{{sonuc}}” yazdığımız yerde ()parantez kullanmadık. Çünkü, dediğimiz gibi computed property’si fonksiyon olarak çalışır ancak property olarak tanımlanır.

Watch

Computed senkron çalışan bir yapıdır. Yani computed’da çıkan bir sonuç anında ekrana yazdırılır. Ancak data içerisinde bulunan herhangi bir property’yi izlemek için yani preoperty değiştiğinde devreye girecek asenkron bir reactivity property kullanmak isterseniz watch’dan yararlanmanız gerekir.

<div id="deneme6">
<button v-on:click="sayi++">Arttır</button>
<button v-on:click="sayi--">Arttır</button>
<button v-on:click="sayi2++">sayi2 Render denemesi</button>
<p>sayi : {{sayi}} | {{sayi2}}</p>
<p>{{sonuc}}</p>
</div>
<script>
new Vue({
el: "#deneme6",
data: {
sayi: 0,
sayi2: 0
},
methods: {
(içi boş)
},
computed : {
sonuc : function(){
console.log("computed çalıştı...")
return this.sayi > 10 ? "10 dan büyük" : "10 dan kucuk";
}
},
watch : {
sayi : function(value){
getThis = this
setTimeout(function(){
getThis.sayi = 0;
},1500)
}
}

})
</script>

Data içerisinde izlemek istediğiniz property’yi watch’ın içerisine aynı isimde function olarak tanımlıyorsunuz. Bu fonksiyon varsayılan olarak kendi içerisinde bir “value” barındırıyor. Örneğin burada watch’ın çalışma mantığını anlamak adına bir settimeout tanımladık. Sayi’nın değeri değiştiğinde watch devreye girecek ,1.5sn sonra settimeout çalışacak ve sayi’nin değerini sıfırlayacak. Burada dikkat ederseniz getThis diye bir değişken tanımladık. Bunun sebebi javascript’den de bildiğiniz gibi iç içe kullanılan fonksiyonlarda scopelar(alanlar) değişir ve this’i kaybedesiniz. Biz settimeout’un içerisinde this i kaybetmemek için getThis adında bir değişken kullandık.

Css classları ile dinamik işlemler

Div’e tıkladığım zaman rengi kırmızı olsun, bir daha tıkladığımızda rengi eski haline dönsün. Bildiğiniz jquery’de toggleClass’ını Vue Js ile yapacağız. Vue Js’de class’a bir bind işlemi yapmak için “v-bind:class” yada kısa yöntemi olan “:class” yazarız. Bu class içerisine {key : value} den oluşan bir oje alır. Key bizim element olarak eklemek istediğimiz class’ın ismidir. Value kısmı ise sağlanan şarta göre alacağı değerdir.

    <style>
.box {
width: 100px;
height: 100px;
background-color: #f2f3f8;
display: inline-block;
margin-right: 5px;
}
.red {
background-color: #f4516c;
}
</style>
<div id="deneme7">
<div
@click="classEkle = !classEkle"
:class="{red : classEkle}" class="box"></div>

<div class="box"></div>
<div class="box"></div>
</div>
<script>
new Vue({
el: "#deneme7",
data: {
classEkle : false
}
})

</script>

Örneğin burada {red : true} yaparsak , bunu yaptığımız kutular sürekli kırmızı olarak kalacaktır. Bu nedenle bizim bu value kısmını bir koşula bağlamamız gerekmektedir. Buradaki sınıflar kafanızı karıştırmasın. “class” olarak tanımladığımız element ile birlikte gelen, “:class” olarak tanımladığımız ise render işlemi sonrası Vue JS’e bind ettiğimiz sınıftır. Vue Js render sonrası sınıfların farklı olduğunu anlar ve oluşturduğunuz aksiyonlara göre tek bir class altında birleştirir. Burada yaptığımız işlem varsayılan özelliği false olan “red” için bir değişken set etmek ve o değişkenin değeri değişince “true yada false” olarak geri dönmek. Bunun tıkladığımızda her seferinde işlemin tersini yapacak bir click event oluşturduk.

Kullanım açısından kolay görünse de template üzerinden script tanımlamak profesyonel yapıya karşı bir durum. Bu nedenle classEkle’nin durumuna göre değişecek dinamik reactif bir yapıya ihtiyacımız var.

<style>
...
.red {background-color: #f4516c;}
.blue {background-color: #00c5dc;}
</style>
<div id="deneme7">
<div
@click="classEkle = !classEkle"
:class="divClass" class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
<script>
new Vue({
el: "#deneme7",
data: {
classEkle : false
},
computed : {
divClass : function(){
return {
red : this.classEkle,
blue : !this.classEkle
}
}
}

})
</script>

Aynı yapıyı sadece divClass adında bir objede toplayarak etkileşimi sağlayabiliriz. Burada blue’nun görevi, kutuya tıkladıktan sonra aktifliğini kaybederse blue tekrardan aktif olduğunda kırmızı olması için. Dinamikliği biraz daha üst seviyeye taşımak için aşağıdaki gibi bir yapı da kurabilirsiniz.

<style>
.box {width: 100px; height: 100px; background-color: #f2f3f8; display: inline-block; margin-right: 5px;}
.red {background-color: #f4516c;}
.blue {background-color: #00c5dc;}
.green {background-color: green;}
</style>
<div id="deneme8">
<div class="box" :class="color"></div>
<div>
<input type="text" v-model="color">
</div>
</div>
<script>
new Vue({
el: "#deneme8",
data: {
color : "green"
}
})
</script>

Bir ccs class’ını koşula bağlı olmadan bind etmek ve kontrol altına almak isteyebiliriz. Yukarıdaki örneğimiz tam olarak bunu anlatıyor. Input’umuza tanımladığımız color elementi two way ile aynı zamanda box class’ının class property’sine bağlı. İlk anda input’umuz instance’ın içerisinde color’ı green olarak aldığı için başlanğıç value’su green olacak. Aynı zamanda box div’imizin class’ı da data içerisindeki color’a baktığı için o da green olacak. İşte bu noktada dikkat ederseniz herhangi bir şekilde obje notasyonuna maruz bırakmadan veya koşul koymadan direkt class’ın adını vererek de bağlama işlemi yapabiliyoruz. Bu bize başka bir yapı da eğer aynı data objesi üzerinden işlemler yapıyorsa tek bir elden bind işlemi yaparak kontrolü sağlamış oluyorsunuz.

Yukarıdaki örnekte aynı mantıkda çalışacak şekilde style da tanımlayabilirsiniz.
<div class=”box” :class=”color” :style=”background-color” : color ></div>

Koşullar ve Listeler

Web sayfamızda belirli bir bölmeyi belirli bir koşula bağlı olarak göstermek istiyor yada istemiyoruz olabilirsiniz yada bir array’iniz var ve bu array içerisindeki elemanları tek bir kodla listelemek istiyorsunuz. İşte bu ve bunun gibi durumlar için hangi direktifleri kullanacağımızı inceleyeceğiz.

V-if / V-else / V-else-if

Burada öncelikle koşulumuzun kontrolünü sağlayabilmek için sonuç üreten bir yapı oluşturmamız gerekiyor. Bu yapıyı data da,methods da yada computed da nerede kurgulayacağız önemli değil. V-if direktifi karşıladığı true veya false değerine göre aksiyon alır. Örneğin “v-if=false” yazarsak “true olursa göster false olursa gösterme” demiş oluyoruz. Yani bunu tanımladığımız div gösterilmeyecektir. Buradaki false alanına javascript kodu da yazabilirsiniz. Ancak o kodun sadece “false-true” çıktısıyla ilgilenir.

<div id="deneme8">
<p v-if="show">Yazı 1</p>
<p v-else>Yazı 2</p>
<button @click="show = !show">Goster / Gizle</button>
</div>
<script>
new Vue({
el:"#deneme8",
data : {
show : false
}
})
</script>

Örneğin bir buton etkileşimi ile iki yazının gösterip gizlenmesi şeklinde bir örnek olsun. Burada amaç bir butona bastığımızda yazının birinin gösterilip diğerinin gösterilmemesi. Kısacası iki yazının görünürlüğü arasında ters bir ilişki olması. Açılış anında data instance’ında show’un karşılığı false olduğu için ilk yazı görünmeyecektir. Butona basıldığında ters gösterimin olması için Vue-Js’nin if ile kullanılan bir direktifi olan v-else’den yararlanıyoruz. V-else burada v-if ‘in koşulu sağlanmadığı zaman devreye giren bir direktiftir.

Tüm yazılım dillerinde olduğu gibi Vue Js’de de else if kullanımı da mevcut.

<div id="deneme8">
<p>Count : {{count}}</p>
<p v-if="count < 10">10 dan kucuk</p>
<p v-else-if="count > 10">10 dan buyuk</p>
<p v-else>10 a eşit</p>
<button @click="count++">Arttır</button>
</div>
<script>
new Vue({
el: "#deneme8",
data: {
count : 0
}
})
</script>

Örneğin bir count’umuz olsun ve butona bastıkça değerini arttıralım. Butona her bastığımızda koşulların sağlanma durumuna göre belirlediğimiz koşullar devreye girecek ve ona göre gösterilip gizlenecektir.

V-show

V-if direktifine alternatif olarak kullanılır. Bu direktif v-if’den iki tane farkı vardır. Birincisi; “v-else / v-else-if” direktifleri ile ortak çalışamaz. Sadece gösterme ve gizleme etkileşimi içindedir.

<div id="deneme9">
<p v-show="goster">Yazi</p>
<button @click="goster = !goster">Göster / Gizle</button>
</div>
<script>
new Vue({
el: "#deneme9",
data: {
goster : true
}
})
</script>

İkinci ve bence en önemli farkı ise gizlenen veri DOM’dan kaybolmaz sadece style kısmına “display:none” ekleyerek gizler. Yukarıda v-if’i anlatırken kullandığımız örneğin DOM çıktısına bakacak olursak;

Üç koşul koymamıza rağmen sadece o anki koşulun karşılığı DOM’da yüklenmiş durumdadır. Diğer koşullar karşılanana kadar hiçbir şekilde DOM’a yüklenmeyecektir. Yalnız v-show kullanımında datanız DOM’da yüklenecek ve sadece css ile gizlenmiş olacaktır.

V-for

Kendini tekrar eden yapılarda kullanılır. Örneğin bir ürün array’imiz olsun. İçerisindeki dataları ekrana yazdırmak istiyorsak hepsini tek tek yazmak yerine sadece v-for direktifi ile tek seferde hepsini listeleyebiliriz.

...
<li v-for="urun1 in urunler1">{{urun1}}</li>
...
data: {
urunler1: ["urun 1", "urun 2", "urun 3", "urun 4"]
}
...

İlk olarak gezeceğimiz objenin tüm elemanlarına vereceğimiz ismini(urun1), ardından son kısımda da data içerisinde neyi listelemek istediğinizi yazmalıyız(urunler1). Biz bunu yazdığımızda v-for ilk olarak o datanın içinde bulunan bütün elemanları gezecek ve her bir elemana urun1 ismini verecektir. Sonrasında elemanlara verdiğimiz ismi yazmamız yeterli olacaktır.

Aynı örnekte listelediğiniz datanın her bir elementinin index’ini almak isterseniz listeleme sırasında “x” yerine herhangi bir değişken ismi girerek index’lerini yazdırabilirsiniz.
<li v-for=”(urun1, x) in urunler1">{{urun1}} - {{x}}</li>

V-for ile listeleme yaparken aynı diğer dillerdeki for döngüsü gibi kullanabilirsiniz. Örneğin 1'den 5 e kadar rakamları yazdırmak için sadece aşağıdaki gibi ufak bir kod yazmak yeterli olacaktır.

<body>
<div id="deneme12">
<label v-for="i in 5"> {{i}} </label>
</div>
</body>
<script>
new Vue({
el: "#deneme12"
})
</script>
OutPut
1 2 3 4 5

Template

Vue Js’tarafından render edilmeyen bir elementdir. Bu element render edilmeyip DOM’a basılmadığı için listeleme sırasında html yapınızı bozmamak için kullanabilirsiniz.

V-for kullanırken iç içe for döngüleri kullanarak objeleri ve içerisindeki elementleri de listeleyebilirsiniz.

...
<div id="deneme11">
<div v-for="urun in urunler2">
<p v-for="(value,key,index) in urun">
<label>
<b>{{key}}</b>
</label>
<span>
{{value}}
</span>
</p>
</div>
</div>
<script>
new Vue({
el: "#deneme11",
data: {
urunler2: [
{ urunIsim: "b1", urunAciklama: "c1" },
{ urunIsim: "b2", urunAciklama: "c2" },
{ urunIsim: "b3", urunAciklama: "c3" },
{ urunIsim: "b4", urunAciklama: "c4" }
]

}
})
</script>
...

V-for çalışma mantığı ve key kullanımı

Vue Js v-for ile döndürdüğü elemanların pozisyonlarını aklında tutar. Yani elementin kendisini tutmaz. Bu aslında güzel bir çalışma mantığı olsa da eğer döngüye soktuğunuz ve listelediğiniz elementlerin yerlerini değiştirdiğinizde v-for index üzerinden çalıştığı için kafası karışıp hataya sebep olabiliyor. Bu hatayı önlemek için “v-bind:key” özelliğini kullanmamız gerekiyor. Key olarak belirleyeceğimiz değerin eşsiz olması güvenlik açısından önemli.

...
<div id="deneme10">
<div v-for="(urun1,index) in urunler1" :key="urun1">
<h3>{{urun1}}</h3>
<p>{{index}}</p>
</div>
</div>
<script>
new Vue({
el: "#deneme10",
data: {
urunler1: ["urun 1", "urun 2"]
}
})
</script>
...

Yukarıdaki daha önce yaptığımız örneğin aynısını kullanarak sadece :key=”urun1” key özelliği bind ediyoruz. Çıktıda hiç bir farklılık olmasa da bu sefer kullandığımız ürünlerin index’lerini değil döndürdüğümüz ürünlerin isimlerine göre sıralıyoruz. Aslında isimler de benzersiz olmayabilir. Sonuçta aynı isimde başka nesneler de olabilir. Bu nedenle en güzel yöntem listelediğiniz datada bir “id” barındırmak olacaktır. Böylelikle key kısmına karşılık id’yi tanımlayabilir ve benzersiz bir elementle daha güvenli listeleme işlemleri yapabilirsiniz.

Vue Instance’ı nasıl çalışır ?

Vue Js DOM ve Javascript içerisinde bağlantıyı kuran orta adam(middle man) elemanıdır. Html içerisinde tanımlayacağımız bütün tanımlamaları ve aksiyonları yaptığımız yer Vue instance’dır. Şu ana kadar Vue instance’ını oluştururken el, data, methods, computed ve watch property’lerinden yaralandık. Bu property’ler bizim Vue Js uygulamamızı yapmamızı sağlayan temel property’lerdir. Şu ana kadar gördüğümüz konularda kafanıza iki soru takılmış olabilir;
1) “Hep bir tane Vue template’i üzerinden bir Vue instance’ı kontrol ettik. Peki birden fazla Vue instance’ı oluşturulabilir mi ? ve farklı template’ler kontrol edilebilir mi?”
2) “Vue instance’ına dışarıdan javascript ile müdahale edebilir miyiz ?”
Bu iki sorunun cevabı da “Evet”.

...
<div id="app1">
{{title}}
</div>
<div id="app2">
{{title}}
</div>

<script>
new Vue({
el: '#app1',
data: {
title: 'VueJS Instance 1',
}
});
new Vue({
el: '#app2',
data: {
title: 'VueJS Instance 2',
}
});
</script>
...

Oluşturduğunuz her bir Vue instance’ı ayrı birer uygulamadır. Bu sayede büyük projelerde Vue Js kullanabileceğiniz gibi aynı zamanda küçük uygulamalar veya widget’lar da yapabilirsiniz. Yani yukarıdaki örneğe bakacak olursanız app 1 ve app 2 adında ayrı iki uygulama vardır ve arasında herhangi bir bağlantı yoktur. Bu durumda this ile bir instance’dan diğerine erişemezsiniz. Tabiki yazdığımız kod javascript olduğu için bu erişim yapılamaz bir şey değildir.

...
<script>
var vue1 = new Vue({
el: '#app1',
data: {
title: 'VueJS Instance 1',
}
});
var vue2 = new Vue({
el: '#app2',
data: {
title: vue1.title,
}
});
</script>
...

Ancak bunu yaptığımızda Vue Js’in işleyiş biçimine aykırı bir durum sergilemiş olursunuz. Eğer iki instance arasında veri alışverişi yapacaksanız yani bir etkileşime geçecekseniz en güzeli ikisini bir instance altında toplamak olacaktır. Vue Js kodlarken bunu ayrı bir dil olarak veya her şeyin Vue instance’ları üzerinden gittiğini düşünmeyin. Kodladığınız her şey javascript. “new Vue({})” ile oluşturduğunuz aslında bir javascript objesi. Doğal olarak bu tam anlamıyla dışarıdan erişilebilen bir yapı.

...
<script>
var vue1 = new Vue({
el: '#app1',
data: {
title: 'VueJS Instance 1',
}
});
setTimeout(function(){
vue1.title = "timer başlığı 2 saniye sonra değiştirdi."
},2000)
var vue2 = new Vue({
el: '#app2',
data: {
title: vue1.title,
}
});
</script>
...

Vue Js’e oluşturduğumuz javascript objesini gönderdiğimizde arka tarafta Vue JS’nin constructor’una parametre göndermiş oluyoruz. Bu obje parametresinin herhangi bir sınıfa gönderdiğimiz parametreden hiçbir farkı yok. Bu parametreyi Vue Js kendi anlayacağı şekilde dönüştürüyor. Bu dönüştürme işlemi sonrasında biz buradaki objeleri Vue instance ve Html içerisinde rahatlıkla kullanabiliyoruz. Vue Js bu kendi okuyacağı şekle dönüştürmesinden sonra ona sağlamış olduğumuz tüm property’leri watcher layer denen bir ara katman sayesinde izlemeye başlıyor. Bu izleme sayesinde data,methods..vb property’lerde veri değişiminde ilgili alanın template’i render ediliyor ve DOM üstüne basılıyor.
Başından beri dediğimiz gibi Vue Js kendine has bir dil değil aslında javascript yazıyoruz. Ancak Vue’nin özelliklerinden yararlanmak için onun belirlediği yoldan yürümemiz gerekiyor. Örneğin Vue objemize dışarıdan ayrı bir property tanımlayalım ve Vue objemizi console da yazdıralım.

...
<div id="ornek12">
{{baslik}}
</div>
<script>
var vue1 = new Vue({
el: '#ornek12',
data: {
baslik: 'VueJS Instance 1',
}
});
vue1.yeniOzellik = "Test !";
console.log(vue1);
</script>
...

Dikkat ederseniz bizim tanımladığımız “yeniOzellik” property’si ve Vue constructor’ına parametre olarak gönderdiğimiz “el” objesi görebilirsiniz. Yalnız burada aslında bir sıkıntı var. Dışarıdan property tanımlayabilirsiniz ancak dışarıdan tanımladığınız bir property’yi Vue js instance’ı create olduktan sonra dahil edemezsiniz. Yani watcher layer katmanı bu property’i tanımaz ve re-render işlemine dahil etmez. Çünkü Vue Js bunu kendi içinde proxy edemez yani bağlantı kuramaz.

Watcher layer Vue Js create olduktan sonra sadece içerisinde bulunan objelerle bağlantı kurabilir ve bu bağlantı sonrasında “get/set” işlemleriyle aksiyon alabilir. Örneğin yukarıdaki console çıktısında aşağıya bakacak olursanız Vue js içerisinde tanımladığımız “başlık” verisini görebilirsiniz. Burada şeffaf şekilde görünmesinin sebebi bunu biz bu noktaya koymadık. Bu verinin bu şekilde yazılmasını sağlayan watcher layer’dır. Bu sayede watcher layer nerede değişiklik olup olmadığını anlıyor ve re-render işlemini gerçekleştiriyor. Dikkat ederseniz “yeniOzellik” ile ilgili hiç bir “get/set” işlemi bulunmamaktadır.

$el $refs ve $data objeleri

$el : Vue js bütün template işlemini el objesi’si üzerinden yapar.
$data : Bu obje bizim Vue instance içerisinde tanımladığımız data property’sini veriyor. Yani örnek verecek olursak; Vue instance içerisinde tanımladığınız title’a console’dan vue.title ile ulaşabileceğiniz gibi aynı zamanda vue.$data.title ile de ulaşabilirsiniz. Çünkü ikisi de aynı yere bakıyor.
$refs : Herhangi bir html tagına eklendiğinde bulunduğu html tagının elementlerine erişebilirsiniz.

...
<div id="ornek13">
{{baslik}} <br>
<button ref="myButton">Ref Örneği</button>
</div>
<script>
var vue1 = new Vue({
el: '#ornek13',
data: {
baslik: 'VueJS Ref Örneği',
}
});
console.log(vue1);
...

$Refs Vue instance’ının içerisinde bir obje olduğu için this.$refs… diyerek erişim de sağlayabiliriz. Örneğin yukarıdaki örneğimize ekleme yaparak butona tıklandığında isminin değiştirilmesini ref üzerinden yapalım.

...
<div id="ornek13">
{{baslik}} <br>
<button @click="show" ref="myButton">Ref Örneği</button>
</div>
<script>
var vue1 = new Vue({
el: '#ornek13',
data: {
baslik: 'VueJS Ref Örneği',
},
methods : {
show : function(){
this.$refs.myButton.innerText = "Deneme"
}

}
});
console.log(vue1);
...

Gördüğünüz gibi ref yazdığımızda attribute’un değerini $refs ‘in içerisine taşıyor ve bizim üzerinde geliştirme yapmamıza olanak sağlıyor. Burada $refs’i kullanırken dikkat etmemiz gereken bir nokta var;

...
<div id="ornek14">
<h1 ref="myTitle">{{title}}</h1>
</div>
<script>
var vue1 = new Vue({
el: '#ornek14',
data: {
title: 'baslik'
},
methods: {
changeH1: function () {
this.title = "baslik değişti";
}
}
});
setTimeout(function () {
vue1.changeH1()
}, 1000);
vue1.$refs.myTitle.innerText = "Refs başlığı değiştirdi";
</script>
...

Örneğin bir başlığımız ve o başlığın değişmesini sağlayan “changeH1” adında bir fonksiyonumuz olsun. Burada refs ile dışarıdan müdahale ettiğimizde ilk olarak sayfa açılışında başlığımız “Refs başlığı değiştirdi” olacak ardından 1sn sonra “baslik değişti” olarak güncellenecektir.

Burada SetTimout sadece sıkıntıyı fark edebilmeniz için kullanıldı.

Yani refs ile yapıya müdahalemiz etkisiz kalacaktır. Bunun sebebi bizim title’ımızın içerisinde instance ile ilişkili bir yapımız var. Yani daha önce de belirttiğimiz gibi Vue Js instance’dan yola çıkarak template ile beraber bir rendering işlemi yapar. Bu render işlemi bittikten sonra oluşturduğu şablonu DOM’a gönderir ve günceller. Kısacası $refs ile html üzerindeki native elementlere erişebilirsiniz ancak, $refs ile yaptığımız herhangi bir aksiyon instance içerisinde yer almadığı için watcher layer tarafından ele alınmaz ve render işlemine dahil edilmeyecektir.

Template mount etme işlemi

Bu zamana kadar gördüğümüz işlemlerde “id” si “app” adında bir kapsayıcı “div” ile yapılar oluşturduk. Bu kapsayıcı “div” aslında içerisinde bulunan elementlerle ilgili aksiyon almamızı sağlıyor. Aslında bizim yaptığımız şey aslında bir template. Vue Js gidiyor bizim belirttiğimiz div yapısını alıyor kendi içerisinde kendi anlayabileceği bir Javascript’e dönüştürüyor. Daha sonra DOM’a bunu gönderip Native Html olarak yapıştırıyor. Peki bize bu yapıyı oluşturmamızda ön ayak olan “el” property’sini silersek ne olur?

...
<div id="app15">
<h1>Örnek Başlık : {{title}}</h1>
</div>
<script>
var test1 = new Vue({
//el:"#app15",
data: {
title: "Deneme başlık",
},
});
console.log("test1 nerede", test1);
</script>
...

Gördüğünüz gibi sadece süslü parantezlerimiz kaldı. Bunun sebebi Vue Js ile biz artık “app15” alanını seçmedik ve kontrol altına almadık. Şu an yazdığınız kod patlamış gibi gözükse de aslında Vue instance’ı oluştu.

Console’u açtığımızda belirttiğiniz “title” property’sini görebilirsiniz. Tek farkı kullanım için “$mount()” şeklinde kullanılan bir method’dan yararlanacağız. “$mount()” bizim oluşturmuş olduğumuz bir instance’a bir template’i bağlamamızı sağlar. “el” property’sini kaldırdığımız için bu instance herhangi bir şablon üzerinden değil, içerisindeki verilerle yaratıldı. Fakat bunu DOM üzerinde kontrol altına almamız ve çalıştırmamız için bind etmemiz yani mount işlemi yapmamız gerekiyor. “$mount()” içerisine bir tane parametre alır. Burada da bu parametre tabiki bizim “#app15” isimli “el” parametremiz olacaktır.

...
<div id="app15">
<h1>Örnek Başlık : {{title}}</h1>
</div>
<script>
var test1 = new Vue({
//el:"#app15",
data: {
title: "Deneme başlık",
},
});
test1.$mount("#app15");
</script>
...

Bu kullanıma ihtiyaç duymamızın en büyük sebebi; Html yapınızın component gibi çalışmasına olanak sağlarsınız. Bu kullanımın farklı bir yöntem olan “template” property’si ile de kullanabiliyoruz.

...
<div id="app16"></div>
<script>
var test2 = new Vue({
template: "<h1>Deneme yazısı</h1>",
});
test2.$mount("#app16");
</script>
...

Bu kullanımı araştıracak olursanız “string template” olarak araştırabilirsiniz. İleride “file template” yapılarını inceleyeceğiz. Çünkü “string template” yapılarının bazı kısıtlamaları oluyor. Hatırlarsanız “template mount” konusunun başında “el” property’sini kaldırsanız bile Veu instance’ının oluştuğunu incelemiştik. Bunu şu şekilde de kanıtlayabiliriz.

...
<div id="app16"></div>
<script>
var test2 = new Vue({
template: "<h1>Deneme yazısı</h1>",
});
test2.$mount();
document.getElementById("app16").appendChild(test2.$el);
</script>
...

“test2.$mount()” diyerek artık biz template’imizi yaratmış olduk. Biliyorsunuz bu tür kütüphanelerin kökü Javascript olduğu için nimetlerinden yararlanabiliyoruz. Javascript ile ilgili div’i seçerek bizim template’e ulaşmamızı sağlayan “test2” property’sini kullanarak DOM’a ekleme yapabiliyoruz. Tabi bu çok fazla kullanılan bir yol değil. Amaç burada sadece esnekliğinizi görebilmeniz adına bir örnek oluşturmak.

Component ve Kullanımları

Oluşturduğunuz bir yapıyı farklı yerlerde kullanmak için kopyala/yapıştır yapmaktansa, tek bir yerde yaratıp kullanacağınız sayfalarda sadece çağırmanızı sağlayan yapılara component’lerdir. Hatırlarsanız “template mount” konusunda “template:” property’si içerisinde gördüğümüz elementleri ya “$mount()” ile yada “id”sinden yakalayarak “el” ile tanımlayabiliyorduk. Aşağıdaki örnek gibi;

...
<div id="app17"></div>
<script>
var test3 = new Vue({
el: "#app17",
template: "<h1>Deneme yazısı</h1>",
});
</script>
...

Bu kod temelde aslında bize şunu anlatıyor; “app17” yapısını bul ve yerine bizim “template” property’si içinde bulunan yapı ile değiştir. O zaman nasıl olsa “el” ile değişiyorsa bu kodu yukarıdaki gibi yazmakla aslında aşağıdaki örnek gibi yazmak arasında bir fark bulunmuyor.

...
<app17></app17>
<app17></app17>
<app17></app17>
<app17></app17>
<script>
var test3 = new Vue({
el: "app17",
template: "<h1>Deneme yazısı</h1>",
});
</script>
...

Dikkat ederseniz çıktımızın dört tane olmasını beklerken sadece bir tane geldi. Hatırlarsanız templatelerin bazı kısıtları olduğundan bahsetmiştik. Bu kısıttı aşabilmemiz için component’lerden yararlanmamız gerekiyor.

...
<div id="app17">
<merhaba></merhaba>
<merhaba></merhaba>
<merhaba></merhaba>
</div>
<script>
Vue.component("merhaba", {
template: "<h4>İlk Component</h4>",
});
var vm1 = new Vue({
el: "#app17",
});
</script>
...

Component bir method gibi çalışır ve içerisine iki adet parametre alır. 1. si erişmek istediğiniz elementin adı ve 2. si ise obje ise bizim template’imizin ne olacağı. Aslında bu template Vue instance’ımızdan hiçbir farkı yok. Burada sadece “Vue.component” diyerek global bir component tanımlamış olduk. Şuan bizim burada yaptığımız yapı aslında hala bir “string template”. Kullanım kolaylığı olsa da hala kısıtlara takılma ihtimali bulunmakta. Örneğin bazı browserlarda “<merhaba></merhaba>” tag’ının ilk harflerini büyük yaparsanız çalışmama gibi kısıtlara takılabiliyor ya da “template:” property’sine çok uzun bir html yapısı yazacak olursak kodu okumamız çok zor olacaktı. Bu ve bunun gibi sorunların çözümünü component’leri incelerken derinlemesine göreceğiz.

Yaşam Döngüsü (LifeCycle)

Vue Js’in çalışma mantığını şu şekilde özetleyebiliriz; Herşey biz “new Vue()” dediğimiz anda aslında başlıyor ancak Vue instance’ı yatılmamış oluyor. Bu aşamada verilerimiz daha yaratılmadan önce kontrol altına almamız gereken bir şeyler varsa “beforeCreate()” metod’u içerisinde yapabiliyoruz. Ardından verilerimiz oluşturuluyor. Yani bu noktada eğer bir şey yapmak istersek “create()” metodundan yararlanmamız gerekiyor. Ardından “el” olarak yakaladığımız html template’imiz oluşuyor ama hala kullanıcı bizim oluşan bu yapımızı hala göremiyor. Kullanıcıların bu html yapımızı göremesinden hemen önce yapmak istediğimiz bir adım var ise burada “beforeMount()” metodundan yararlanmamız gerekiyor. Daha sonra “el” ile yakaladığımız yapı artık değiştiriliyor(Ör;{{title}} yazan alanların içleri doluyor). Yani sonunda kullanıcı yüklenmiş olan DOM’u görebilir hale geliyor. Tabi siz datalarda değişiklikler yapıyorsunuz yada etkilişimlere giriyorsunuz. Bu aşamada Vue datalarınız değişimlerini aynı React’de olduğu gibi Virtual DOM ile algılıyor ve sadece datalardaki değişiklikleri değiştirecek “re-render” işlemini tetikliyor. Bu datalardaki değişikliklerin olma aşamasından hemen önce bir değişiklik yapmak istiyorsak burada “beforeUpdate()” metodundan yararlanıyoruz. Tabiki bu “re-render” işleminin sonunda DOM güncelleniyor yani update oluyor. Bu noktada da “update()” metodundan yararlanıyoruz.

Virtual DOM :
Vue Js’in oluşturduğu sanal DOM dur. Sayfa içinde sadece değişiklik olan kısmının render olmasına yardımcı olur.
Virtual DOM
hakkında detaylı bilgi edinmek için "A’dan Z’ye React — Türkçe kaynak" makalemden "DOM & Virtual DOM" başlığını aratarak inceleyebilirsiniz.

...
<div id="app18">
<h1>{{title}}</h1>
<button @click="title='yeni yazı'">Değiştir</button>
<button @click="durdur">Destroy</button>
</div>
<script>
new Vue({
el: "#app18",
data: {
title: "yaşam döngüsü örnek",
},
methods: {
durdur: function () {
this.$destroy();
},
},
beforeCreate: function () {
console.log("beforeCreate çalıştı");
},
created: function () {
console.log("created çalıştı");
},
beforeMount: function () {
console.log("beforeMount çalıştı");
},
mounted: function () {
console.log("mounted çalıştı");
},
beforeUpdate: function () {
console.log("beforeUpdate çalıştı");
},
updated: function () {
console.log("updated çalıştı");
},
destroyed: function () {
console.log("destroyed çalıştı");
},
});
</script>
...

Gördüğünüz üzere sırasıyla çalıştılar. Update ve destroy’u görebilmek adına bir method tanımladık. Sadece burada ekstra söyleyebileceğimiz “durdur” metodu özelinde, hatırlarsanız dolar işareti ($) ile başlayan propertyler bize Vue instance’ı tarafından sağlanan özel property’lerdir. Burada destroy bir metot ve dolayısıyla Vue instance’ımızı yok etmesi için bu şekilde kullandık. Destroy çalıştıktan sonra artık Vue ile javascript bağlantınız kopacaktır. Yani artık Vue kullanılamaz hale gelmiş olur.

Buraya kadar olan çalışmaların tümünde yukarıya bir script tag’ı ile Vue Js’i entegre ettik ve çalışmaya başladık. Yani bu zamana kadar sadece küçük uygulamalar geliştirmek için Jquery kullanırmış gibi örnek yapılar üzerinden ilerledik. Ancak daha büyük uygulamalar yapmak istiyorsak daha farklı bir yapıya geçmemiz gerekir. Bundan sonraki örnekleri Vue CLI (command line interface) ile development server üzerinden devam edeceğiz.

CLI Kurulumu

Kuruluma başlamadan önce bildiğiniz üzere bu paketlerin indirilmesi veyönetimi için bilgisayarınızda Node Js kurulu olmalı. Nasıl kuracağınız hakkında bir bilginiz yok ise “GitHub ve NPM için paket oluşturmak — Türkçe Kaynak” makalemden “Kurulum işlemleri” başlığından adımları izleyebilirsiniz.

npm i -g vue-cli
vue init webpack-simple **proje adını**
sorulan tüm sorulara Enter’a basıp geçebilirsiniz.
cd **proje adını**
npm i
npm run dev

Yükleme işlemi tamamlandığında karşınıza çıkan klasör yapısı bu şekilde olacak. Bu dosyaların ne olduğunu bilmiyorsanız ufak notlar ile açıklayalım.

Node_modules: nmp i yaptığınızda projenizin bağımlı olduğu paketlerin indirildiği klasördür.

Src klasörü: Bütün Vue Js kodlarımızı yazacağımız klasör. Bu klasör ve içeriklerini detaylı aşağıda anlatacağım.

BabelRc : EcmaScript 6 ‘yı EcmaScript 5’e yarayan yapının ayar dosyası.

index.html: projemizi canlıya çıktığımızda karşılaşılan dosya. İçinde bulunan “#app” div’i bizim src klasörü içerisinde yaptığımız aksiyonları ele alan ana root yani çatı alanımız diyebiliriz. Yine içinde bulunan “build.js” ise; geliştirilen tüm uygulamaların günün sonunda tek bir Javascript çatısı altında toplandığı yer.

Package-json: “Vue init” dediğimizde yarattığımız dosyadır. Bu dosya sayesinde internet üzerinden development server’ın ihtiyacı olan bütün paketleri indirmeye sağlayan bir dosyadır. Aslında bir referans dosyası olarak düşünebilirsiniz.

Bu zamana kadar gördüklerimiz ilk “el” isimli property isimlerini vererek istediğimiz bir bölgeyi konrol altına aldık. İkinci yöntem ise yine “el” tanımlıyorduk, fakat herhangi bir html yazmadan “template:” içerisine yazarak “strink template” yazarak çalışmalar yapıyorduk.

import Vue from "vue";
import App from "./App.vue";
new Vue({
el: "#app",
render: h => h(App),
});

“Main.js” dosyasında sizlere farklı gelecek bir yapı bulunmuyor. “#app” index.html’de bulunan div’i işaret ediyor. Yukarıdaki importlar biri “new Vue” yu kullanmamızı, diğeri ise “App.vue” dosyası ile bağlantı kurmamızı sağlıyor. “Main.js” dosyasında belkide size yabancı gelecek tek şey “render” fonksiyonu ile kullanılan EcmaScript 6 arrow function kullanımı olacaktır. Burada “h” isimli bir fonksiyon var ve içerisine yine “h” isimli bir fonksiyonu yani kendini çağırarak içerisine “App” isminde bir argüman alıyor. Burada amaç “el” property’sinin karşısında bulunan template’i otomatik olarak alarak render etmesi. Bu render Vue Js tarafından sağlanan bir fonksiyondur.

“App.vue”dosyası içerisinde template,script ve style tag’ları bulunmakta. Adlarından da anlaşılacağı gibi html bloklarınızı template’e, Vue kodlarını script’e ve css stil kodlarınızı da style tagları içerisine yazıyorsunuz. Burada hepsi bir arada karışık gelebilir ancak ideal bir “single file template” bu şekilde yazılıyor. Eğer size karışık gelirse style veya scriptleri farklı yerden yönetebilirsiniz. Dikkat ederseniz burada script taglar’ı arasında “export default{}” objesi bulunuyor. Biz file template kullandığımız için Vue instance’ı gibi davranacak bir objeye ihtiyaç duyuyor.Bu obje bizim içerisinde “data,method,computed,component vb.” daha önce görmüş olduğumuz property’leri tanımlamamıza imkan tanıyor. Burada gördüklerimizden ekstra bir kullanım olarak “data” tanımlaması eskiye göre biraz farklı kullanılıyor.

Component

Component yapısı

Burada gördüklerimizden ekstra bir kullanım olarak “data” tanımlaması eskiye göre biraz farklı kullanılıyor. Eski yapımızı hatırlarsanız bir component yaratırken aşağıdaki şekilde yaratıyorduk.

...
<div id="app19">
<test></test>
<test></test>
</div>
<script>
Vue.component("test", {
template: "<p>Merhaba Dünya<p>",
});
new Vue({
el: "#app19",
});
</script>
...

Ancak bu instance içerisinde data ile etkileşim katmak istediğinizde şöyle bir hata ile karşılaşıyorsunuz.

...
<div id="app19">
<test></test>
<test></test>
</div>
<script>
Vue.component("test", {
data: {
title: "deneme",
},
template: "<p>Merhaba Dünya {{title}}</p>",
});
new Vue({
el: "#app19",
});
</script>
...

Eğer bir component kullanıyorsanız data objeniz artık bir obje olarak değil fonksiyon olarak kullanmanız gerekmektedir. Ayrıca sadece fonksiyon olarak kullanmanın haricinde geriye bir obje return etmelidir.

...
<div id="app19">
<test></test>
<test></test>
</div>
<script>
Vue.component("test", {
data: function () {
return {
title: "deneme",
};
},
template: "<p>Merhaba Dünya {{title}}</p>",
});
new Vue({
el: "#app19",
});
</script>
...

Component’leri Local / Global Kayıt İşlemleri

Bir önceki “Component Yapısı” için vermiş olduğumuz örnek Global bir tanımlama şekli. Bazen bazı durumlarda bazı component’lerin belirli yerlerde veya belirli instance’lar içerisinde local olarak çalışmasını isteyebiliriz. Bunun için şu şekilde bir örnek üzerinden ilerleyebiliriz.

...
<div id="app19">
<testcomp></testcomp>
</div>
<div id="app20">
<testcomp></testcomp>
</div>
<script>
var localDenemeComponent = {
data: function () {
return {
title: "deneme",
};
},
template: "<p>Merhaba Dünya {{title}}</p>",
};
new Vue({
el: "#app19",
components: {
testcomp: localDenemeComponent,
},
});
new Vue({
el: "#app20",
});
</script>
...

Burada normalde tanımladığımız “Vue.component” fonsiyonunu kaldırdık ve bir değişkene bağladık. Bu değişken component objemizin tüm özelliklerine sahip olmuş oldu. Geri kalan kısım aslında çok basit. Nerede tanımlamak istiyorsanız “components:” property’sine değişkeninizi atıyorsunuz. “components:” property’si bizim bu Veu instance’ı içerisinde kullanacağımız componentleri tanımlamamızı sağlayan bir property’dir. Buraya birden fazla component de tanımlayabilirsiniz. Sonuç olarak çıktıya bakacak olursak “app19” da bulunan component “app20” de çalışmadı.

Component Oluşturmak

Öncelikle src klasörü altında bir tane “.vue” uzantılı file yaratıyoruz. Şimdilik rahat anlaşılması adına ismine “DenemeComponent” diyelim. Bu component içerisinde style’dan yararlanmayacağımız için kaldırdık.

...
<template>
<div>
<p>
Durum kontrol : {{status}}
</p>
<button @click="statusCahnge">Değiştir</button>
</div>
</template>
<script>
export default {
data: function () {
return {
status: "Kapalı",
};
},
methods: {
statusCahnge() {
this.status = "Aktif";
},
},
};
</script>
...

Burada dikkat etmeniz gereken aynı React’de de olduğu gibi, template içerisine koyacağınız html yapıları her ne olursa olsun kapsayıcı bir tag içerisinde olması gerekiyor. Methods’ları normalde yazarken “deneme: function(){..}” şeklinde yazıyorduk ancak burada “statusChange()” şeklinde de Ecmascript 6 özelliklerinden yararlanarak da yazabiliriz. Sonuçta projemiz çalıştığında otomatik olarak bu kodlar EcmaScript 5’e çevriliyor. Component en basit haliyle hazır. Artık bu component’i global olarak “Main.js” içerisine tanımladıktan sonra istediğimiz yerde kullanabiliriz.

import Vue from "vue";
import App from "./App";
import denemeComponent from "./DenemeComponent";
Vue.component("status-component", denemeComponent);new Vue({
el: "#app",
render: (h) => h(App),
});

Main.jsiçerisinde tanımlamamızın sebebi içerisinde render edilen “#app” root alanımıza bakan ana dizin burası. Vue.component kullanımını daha önce gördüğümüz gibi ilk olarak kullanacağımız html tag ismini yazıyoruz. Sonra eskiden obje tanımlardık ancak artık bir component’imiz olduğu için “denemeComponent” yazmamız yeterli olacaktır. Deneme olarak “App.vue” içerisine component’imizi çağırdığımızda çıktıyı görebiliriz.

<template>
<status-component></status-component>
</template>

Component Kullanımı

Hatırlarsanız daha önce component’leri local olarak da tanımlayabileceğimizden bahsetmiştik. “DenemeComponent” yapısını aynı tutarak, yeni bir “Home.vue” adında bir file yaratalım.

Home.vue 
----------------
<template>
<div>
<status-component></status-component>
<status-component></status-component>
</div>
</template>
<script>
import DenemeComponent from "./DenemeComponent";
export default {
components: {
"status-component": DenemeComponent,
},
};
</script>

Burada daha önce gördüğümüz local component tanımlama yapısı ile “DenemeComponent” component’imizi içeri taşıdık. Burada “status-component” yerine istediğiniz tanımlamayı yapabilirsiniz ancak konuda bağlantının kaçmaması adına aynı isim ile devam ettiriyoruz. Şu an yapımız içinde ana root dosyamız “App.vue” olduğu ve herhangi bir sayfalama yapmadığımız için “Home.vue” yapısını burada tanımlıyoruz.

App.vue 
-------------
<template>
<home-component></home-component>
</template>

App.vueyapısında bulunan bu “home-component” tanımlamasını “main.js” üzerinden belirtiyoruz. Burada aslında “Home.vue” component’ini global, “DenemeComponent” componentini locale indirmiş oluyor. Aslında burada temelde baktığınızda “DenemeComponent” child component, “Home.vue” parent component ve “App.vue” de ana yani root component olarak aşağıdan yukarıya bir hiyerarşi olmuş oluyor.

Main.js 
-------------
import Vue from 'vue';
import App from './App';
import Home from './Home';
Vue.component("home-component",Home)new Vue({
el: '#app',
render: h => h(App)
})

Component İsimlendirme

Componentlere isim verirken development workflow’unuzun yani çalışma alanınızın size sağlanan kullanım kolaylıkları var. Aşağıdaki kullanımların hepsi aslında aynı şeyler. Burada “Footer” yazan kısım tek başına olmasının sebebi EcmaScript 6 ‘nın bir özelliği. Karşısında bulunan value ile key kısmı aynı ise tek başına yazılabiliyor. Dikkat etmeniz gereken en önemli durum; key sensitive bir durum yani büyük küçük harf duyarlılığı var. Aşağıda component adını “footer-component” yazdıysanız yukarıda tanımlarken “Footer-component” ya da “footer-Component” şeklinde tanımlama yapamazsınız.

<div>
<footer-component></footer-component>
<footer-component></footer-component>
<footer-component></footer-component>
<Footer></Footer>
<Footer></Footer>
</div>
</template>
<script>
import Footer from "./components/Footer";
export default {
components: {
"footer-component": Footer,
"footerComponent": Footer,
footerComponent: Footer,
Footer: Footer,
Footer,
},
};
</script>

Scoped kullanımı

Geliştirme yaptığınız dosyaların adı “single file component” yani html,vue ve css aynı yerde olmalı ama daha önce de belirttiğimiz gibi aynı yerde yazmak zorunda değilsiniz. Herkesin mimarisi farklıdır. Ancak, olur da aynı yerde yazarsanız özellikle style özelinde “scoped” adında dikkat etmeniz gereken bir durum var.

App.vue
-----------------
<template>
<div>
<Header></Header>
<Main></Main>
</div>
</template>
<script>
import Header from "./components/Header";
import Main from "./components/Main";
export default {
components: {
Header,
Main,
},
};

Örneğin “Header” ve “Main” adındaiki tane component olsun ve içlerine ayrı ayrı iki adet stil tanımlayalım.

Header.vue 
-----------------
<template>
<div>Başlık</div>
</template>
<style>
div {border: 5px solid green;}
</style>
Main.vue
-----------------
<template>
<div>İçerik</div>
</template>
<style>
div {border: 5px solid red;}
</style>

Çıktıya bakacak olursanız iki div de kırmızı oldu. Bunun nedeni “Main” “Header”’dan daha aşağıda durduğu ve kodlamada en son ne yazdıysanız onun kodu geçerli olacağından kaynaklı. “Header” aşağıda olsaydı onun kodu öncelikli olacaktı. Ancak buradaki durumda eğer yazılan kodların sadece component’e özel olmasını ve genele dokunmasını istemiyorsanız; “scoped” kodundan yararlanmanız gerekiyor.

Header.vue 
-----------------
<template>
<div>Başlık</div>
</template>
<style scoped>
div {border: 5px solid green;}
</style>
Main.vue
-----------------
<template>
<div>İçerik</div>
</template>
<style scoped>
div {border: 5px solid red;}
</style>

Componentler arası iletişim

Parent ve Child ilişkisi

Aslında React biliyorsanız yapacağımız bu işlem orada kullanılan syntax (yazım şekli) ile aynı. Componentler arası iletişime örnek olması için az önceki konularda örnek olarak kullan andığımız “Header.vue ve Main.Vue” üzerinden devam edelim. Burada iletişimi deneyimleyebilmek için öncelikle parent ve child ilişkisinde olan bir yapıya dönüştürmemiz gerekiyor. Yani buradaki senaryoda bir component’in diğerini kapsaması gerekiyor. Bu nedenle ayrı ayrı olan header ve main yapılarını değiştirip sanki “Main.vue” “Header.vue” component’inin alt component’i gibi şekillendirdik.

Header.vue 
-----------------
<template>
<div>
<div>Componentler arasi iletişim</div>
<main icerik="bu içerik header componenti içerisinden geldi"></main>
</div>
</template>
<script>
import Main from "./Main.vue";
export default {
components: {
Main,
},
};
</script>
Main.vue
-----------------
<template>
<div>Yazi alani : {{ icerik }}</div>
</template>
<script>
export default {
props: ["icerik"],
};
</script>

Aslında bu kadar basit. Dışarıdan vereceğiniz atribute verisinin ismi ne ise içeride bulunan child component’inde props property’sinde bulunan array’e verinin adını yazıyoruz. Burada aslında bana bu isimli bir veri gelirse kabul et demiş oluyoruz. Bu noktadan sonra “Main.vue” içerisinde “icerik” props’unu aynı “data” içinden property kullanırmış gibi “this.icerik” yazarak kullanabilirsiniz. Ek olarak “Header.vue” içinde bunu daha önce öğrendiğimiz directive’lerden yararlanarak v-bind:icerik(:icerik) ile de dinamik hale de getirebiliriz. Bize aynı sonucu verecektir.

Header.vue 
-----------------
<template>
<div>
<div>Componentler arasi iletişim</div>
<main v-bind:icerik="desc"></main>
</div>
</template>
<script>
import Main from "./Main.vue";
export default {
components: {
Main,
},
data: function () {
return {
desc: "bu içerik header componenti içerisinden geldi",
};
},
};
</script>

Burada dikkat etmeniz gereken bir durum var. Bazen size gelen props’un hangi tip veri olduğunu karıştırabilirsiniz. Örneğin; biz “Main.vue” da bulunan “icerik” için matematiksel bir işlem yapmak istersek yada Javascript’in rakamsal(integer) veriler için kullanılan özelliklerinden yararlanmak isterseniz console’da hata alırsınız. Çünkü gelen değer string(yazı). Bu gibi durumlarla karşılaşmamak için size gelen props’ları kontrol edebilirsiniz.

Main.vue 
-----------------
<template>
<div>Yazi alani : {{ icerik }}</div>
</template>
<script>
export default {
props: {
icerik: ["String", "Array"],
},
};
</script>

ilk olarak dikkat ettiyseniz props kısmı artık array şeklinde yazılmıyor ve objeye dönüşüyor. Aslında “içerik” alanını da obje olarak yazmamız gerekiyor ancak burada array kullanmamızın sebebi sadece tip belirtecek olmamızdan kaynaklı. Burada “icerik” props’unun “string veya array gelebilir” şeklinde tanımlamasını yaparak kontrol sağlayabiliyorsunuz. Eğer “icerik” özelinde daha fazla kontrol tanımlaması yapmak isterseniz aşağıdaki gibi yine objeye dönüştürerek kontroller sağlayabilirsiniz.

Main.vue 
-----------------
<template>
<div>Yazi alani : {{ icerik }}</div>
</template>
<script>
export default {
props: {
icerik: {
type: String,
required: true,
default: "Bir yazı gelmedi", //bir içerik gelmezse bu yazı gözükür
},
},
};
</script>

“type” az önce bahsettiğimiz tipini sınırlandırdığımız, “required” buraya mutlaka bir yazının gelmesi gerektiğini ve “default” kısmı da hiç yazı gelmezse “bir yazı gelmedi” yazması için oluşturduk. Burada “requied” ve “default” aslında birbiriyle çakışan alanlar. Zaten gelmesi zorunluysa nasıl oluyor da default olarak yazı yazabiliyor :) Tabi bunları sadece örneklendirmek için paylaşıyorum. Yoksa bu şekilde bir durum saçma olacaktır. Ek olarak component’ler arası obje gönderecek olursanız ve obje gelmediği durumda “default” tanımlama yapacaksanız, “default” kısmında bir fonksiyon ile obje return etmeniz gerekmektedir.

Main.vue 
-----------------
<template>
<div>Yazi alani : {{ icerik }}</div>
</template>
<script>
export default {
props: {
icerik : {
type: Object,
default: function (){
return {
...
}
}
}
}
};
</script>

Child ve Parent ilişkisi

Peki bir önceki konuda yapılan etkileşimi yani kapsayıcı compnent’in alt component’e veri akışı işleminin tersini yapmak istersek… Aslında demin yaptığımız iki yönlü iletişimin aynısını ancak farklı argümanlar kullanarak ilerlememiz gerekiyor.

Main.vue 
-----------------
<template>
<div>
<button @click="yukariGonder">Veriyi yukarıdaki component'e gönder</button>
</div>
</template>
<script>
export default {
methods: {
yukariGonder() {
this.$emit("gonderilenData", "Merhaba Dünya !!!");
},
},
};
</script>

“Main.vue” içerisinde bir buton tanımlaması yaptık ve ismine de “yukarıGönder” şeklinde isimlendirdik. Burada amaç main içinde bulunan butona tıklandığında yukarı katmanda bulunan “Header.vue” component’ine “Merhaba Dünya !!!” yazısını göndermek. Aslında buraya kadar görmediğiniz bir durum bulunmuyor. Methods içerisinde fonksiyon tanımlıyoruz. Tek fark “this.$emit” kullanımı. $emit vue instance’ının anlayacağı özel bir method. Hatırlarsanız bir önceki konularda this’den sonra gelen dolar işaretli verilerin vue instance’ının kullandığı ya property yada method’lar olduğunu belirtmiştik. Emit, “yaymak” Türkçe karşılığından da anlaşılacağı gibi veriyi yaymamızı sağlıyor. Burada aslında “ben buradan veri gönderiyorum bu verinin adı da gonderilenData ve verinin karşılığı da Merhaba Dünya!!!”

Header.vue 
-----------------
<template>
<div>
<div>Componentler arasi iletişim {{asagidanGelen}}</div>
<main @gonderilenData="asagidanGelen = $event"></main>
</div>
</template>
<script>
import Main from "./Main.vue";
export default {
components: {
Main,
},
data: function () {
return {
asagidanGelen: "",
};
},
};
</script>

“Header.vue” component’imizde “gönderilenData” adında bir event geleceğini ve bu event geldiğinde bizim data içinde belirlediğimiz bir “asagidanGelen” adında bir property’nin karşılığını doldurmasını istiyoruz. Burada size farklı gelecek kısım “$event” olacaktır. Bu Vue Js tarafından bilinen keyword. $event bizim “Main.vue” dan gelen “Merhaba Dünya !!!” verisine denk geliyor. $event yerine “Deneme” yazsaydık; “Componentler arasi iletişim Deneme” şeklinde bir çıktı alırdık. Çıktıya bakacak olursak butona bastığımızda “Componentler arasi iletişim Merhaba Dünya !!!” yazdığını göreceksiniz.

Bu anlatılanlardan sonra kafanızda şu soru oluşmuş olabilir ; “Peki child component’ler arası veri gönderilebilir mi?” Aslında bu pek mümkün değil. Örneğin, “Header.vue” içinde “Main1.vue ve Main2.vue” olsun. main1 ve main2 arasında veri göndermenin tek yolu veriyi önce “Header.vue” ya göndermek ve bir prop içinde tutmak ardından diğer child component’e göndermek şeklinde olacaktır. Bu durum aslında React ile çok benzerlik gösteriyor. React makalemi okuduysanız orada da component’ler arası iletişim için küçük projelerde aynı şekilde bu yukarıdaki örnek gibi kullanımı, ardından orta ölçekli projeler için ContexAPI kullanımını ki bunun Vue Js’de karşılığı “Event Bus” şeklindedir. Son olarak da büyük projeler için Redux kullanıyorduk. Vue Js’de de büyük ölçekli projelerde componentler arası veri gönderimi için Vuex kullanıyor olacaksınız. Bu makale çok uzun olduğu için Vuex konusunu aynı React/Redux makalelerim gibi ikiye böleceğim. Maalesef bu makalede Vuex ile ilgili bilgi bulamazsınız.

Event Bus

Aslında bizim Vue Js tarafından kullanabildiğimiz bir instance olarak düşünebilirsiniz. Merkezi olarak bu veri akışlarını sağladığımız yardımcı bir yapı. Bu konuyu anlatmadan önce mevcut dosyalar üzerinde değişiklik yapalım ve aşağıdaki gibi bir mimarimiz olsun.

Header.vue 
-----------------
<template>
<div>
<div>Componentler arasi iletişim</div>
<Main1></Main1>
<Main2></Main2>
</div>
</template>
<script>
import Main1 from "./Main1.vue";
import Main2 from "./Main2.vue";
export default {
components: {
Main1,
Main2,
},
};
</script>
Main1.vue
-----------------
<template>
<div>
Main 1
</div>
</template>
Main2.vue
-----------------
<template>
<div>
Main 2
</div>
</template>
main.js
-----------------
import Vue from "vue";
import App from "./App";
export const eventBus = new Vue();new Vue({
el: "#app",
render: (h) => h(App),
});

Burada aslında başlangıç aşamasını “main.js” olarak belirtebilirim. “main.js” içerisinde tanımladığımız “eventBus” instance’ının en tepede olmasının bir sebebi var. Çünkü bir altında bulunan vue instance’ı içerisinde bunu kullanıyor olacağız. Bu nedenle aşağıda yazarsak önce vue sonra eventBus oluşacağı için çalışmayacaktır. Bu arada eventBus bizim belirlediğimiz bir isim. Siz “store”,”globalData” veya “canimDatam” şeklinde de tanımlama yapabilirisniz. Burada asıl amaç “new Vue” ile yeni bir instance oluşturmak ve kullanmak. Header ve main.js dosyalarına dokunmadan main1 ve main2'yi düzenleyelim.

Main1.vue 
-----------------
<template>
<div>
Main 1 <br />
<button @click="datayiGonder">Datayi Gönder</button>
</div>
</template>
<script>
import { eventBus } from "./../main";
export default {
methods: {
datayiGonder() {
eventBus.$emit("gonderilenData", "Merhaba Dunya !!!");
},
},
};
</script>
Main2.vue
-----------------
<template>
<div>
Main 2 <br />
Gelen Data: {{ gelenData }}
</div>
</template>
<script>
import { eventBus } from "./../main";
export default {
data: function () {
return {
gelenData: "",
};
},
created() {
var self = this;
eventBus.$on("gonderilenData", function (event) {
self.gelenData = event;
});
},
};
</script>

Adım adım anlatacak olursak; “Main1.vue” dosyamızda artık datayı eventBus üzerinden tanımlayacağımız için import ile main.js içerisinden çektik. eventBus’ı export default ile dışarıya taşıdığımız için {evetBus} şeklinde süslülerle kullandık. “datayiGonder” isimli bir butona fonksiyon tanımladık. Bu fonksiyona bizim datayı eventBus ile göndermemizi sağlayacak yapıyı oluşturdu. eventBus bir vue instance’ı olduğundan istediğimiz method’lardan yararlanabiliriz, bu nedenle göndereceğimiz datayı bir önceki konuda anlattığımız gibi $emit ile yayılımını sağladık. “gonderilenData” ile key kısmını ve “Merhaba Dunya !!!” ile value kısmını tanımladık. Event’imizi otobüse koyduğumuza göre butona basarak artık diğer “Main2.vue” componentine doğru yolcu edebiliriz.

“Main2.vue” component dosyamızda; LifeCycle konusundan da hatırlayacağınız gibi, bu component üretilmeye başlandığı anda işlem yapacağımız için created() hooks’undan yararlandık ve diğer taraftan gelebilecek emitleri okumasını sağladık. eventBus’ı kullanacağımız için tekrar yukarıya tanımlamasını yaptık. Gönderdiğimiz $emit method’unu kullanabilmek için $on method’undan yararlandık. Burada aynı terminalde yolcu bekler gibi “kimi bekliyorduk? “gonderilenData” yı, bu data gelince ne yapacağım? “gelenData” yı ekrana yazdıracağım” şeklinde bir fonksiyon tanımlıyoruz. Yarattığımız fonksiyonun içinde bulunan “event” bir önceki konuda da gördüğümüz gibi value’yu temsil ediyor. Burada “event” yerine istediğiniz isimlendirmeyi yapabilirsiniz. Ancak burada dikkat etmemiz gereken bir durum var; “var self = this;” yazmazsanız, this’i kaybediyorsunuz. Bunu ya bu şekilde bir değişkene atarak çözersiniz yada Ecmascript ile arrow function yaratarak çözersiniz.

Main2.vue 
-----------------
...
created() {
eventBus.$on("gonderilenData", (event) => {
this.gelenData = event;
});

},
};
...

Tabi kökünde herşey Javascript ve ileride tekrarlı kod yazmamak yada daha düzenli bir kod ile devam etmek isterseniz, “main.js” üzerinde ufak bir oynama ile bu yapıyı tek bir fonksiyon ile yönetebilirsiniz.

main.js
-----------------
...
export const eventBus = new Vue({
methods: {
transferData(data) {
this.$emit("transfer", data);
}

}
});
...
Main1.vue
-----------------
...
methods: {
datayiGonder() {
eventBus.transferData("Merhaba Dunya !!!");
}
}
...
Main2.vue
-----------------
...
created() {
eventBus.$on("transfer", (event) => {
this.gelenData = event;
});
}
...

Slot kullanımı

Component’lere veri gönderebildiğiniz gibi aynı zamanda html yapıları da gönderebilirsiniz. Bunun için slot’lardan yararlanıyoruz. Öncelikle öğrendiklerimizden yola çıkarak çok basit componente props gönderimi yapalım. “Header.vue” kapsayıcı ve “Main.vue” adında kapsanan yani child component oluşturduk. Header’dan “content” adında bir prop ile “Header main alanı” yazısını gönderdik ve main component’inde yazdırdık.

Header.vue
-----------------
<template>
<div>
<header-main content="Header main alanı"></header-main>
</div>
</template>
<script>
import Main from "./Main.vue";
export default {
components: {
headerMain: Main,
},
};
</script>
Main.vue
-----------------
<template>
<div>
<h1>Merhaba Main</h1>
{{ content }}
</div>
</template>
<script>
export default {
props: ["content"],
};
</script>

Burada eğer component elementi görmek isteseydiniz; ilk olarak göndermek istediğiniz elementi component tagları içerisine yazmanız gerekiyor. Ancak bu yeterli olmuyor çünkü main tarafında bunu karşılayacağınız bir ibrare yazmanız gerekiyor. Slot tagı Vue Js tarafından bilinen özel bir etikettir. Slot component’in içerisinde tanımlamış olduğunuz elementleri almanıza ve <slot></slot> ile belirttiğiniz yerin içine yazdırmasını sağlar. Aslında bir bakıma yer değiştiriyor olarak da düşünebilirsiniz.

Header.vue
-----------------
<template>
<div>
<header-main>
<h1>Merhaba Main</h1>
<p>Header main alanı</p>
</header-main>
</div>
</template>
...
Main.vue
-----------------
<template>
<div>
<slot></slot>
</div>
</template>

Burada dikkat etmeniz gereken bir durum var. Tabi uygulamanızda bu şekilde bir talebiniz olabilir ancak, eğer “Main.vue” da bulunan slotları kopyalarsanız gönderdiğiniz html elementleri çoklanacaktır.

Main.vue 
-----------------
<template>
<div>
<slot></slot>
<slot></slot>
</div>
</template>

Bu durumun önüne geçmek istiyorsanız, yani belirli içeriği belirli bir yerde göstermek istiyorsanız slot’larınızı gönderirken ve alırken isimlendirme yapmanız gerekir. Bir önce oluşturduğumuz slot örneğinde “Merhaba Main” alanı farklı slot’da “Header main alanı” yazısının farklı slot’da göstermek için aşağıdaki şekilde yazmanız gerekir.

Header.vue
-----------------
<template>
<div>
<header-main>
<h1 slot="birinciSlotIcin">Merhaba Main</h1>
<p slot="ikinciSlotIcin">Header main alanı</p>
</header-main>
</div>
</template>
...
Main.vue
-----------------
<template>
<div>
<div class="border-1">
<slot name="birinciSlotIcin"></slot>
</div>
<div class="border-2">
<slot name="ikinciSlotIcin"></slot>
</div>
</div>
</template>
<style scoped>
.border-1 {border: 1px solid red;}
.border-2 {border: 1px solid blue;}
</style>

Ek olarak, eğer “Header.vue” alanında bulunan slot=”ikinciSlotIcin” yazısını aldırırsanız ve “Main.vue” da bulunan karşılığını silerseniz. Yukarıdaki çıktıyı yine elde edersiniz. Çünkü slot’larda default kavramı vardır. Eğer siz gönderdiğiniz html elementini isimlendirmezseniz bunun slot’lar içine bir karşılığı yoksa boşta kalanları kendi bir eşleştirme yapar. Aynı mantıkla eğer siz “Main.vue” içine default bir slot tanımlarsanız ve isimlendirirseniz, bunun isim karşılığı “Header.vue” tarafından gelmese bile o slot görünecektir.

Kapsamlı Directive Kullanımı

Directive’leri, component ve elementlere bir iş yaptırmak için kullandığımız küçük anahtar kelimeler olduğundan makalenin en başında bahsetmiştik. Tabi bu anahtar kelimeler tek başına bir anlam ifade etmiyorlar, bunları anlamlandıran arka tarafta bir yazılım bulunuyor. Siz örneğin “v-if” dediğinizde “v-” ile yazılımı etkinleştiriyor ve normal bir attribute’dan ayrımını sağlıyorsunuz. Bunlara kısaca prefix deniyor. “if” yazdığınız kısım sizin directive kısmınız oluyor. Bu durumdan bahsetmemizin sebebi, aslında sizlerin nasıl kendi directive’lerinizi yapacağınızın mantığını anlamanız.

Custom Directive

Vue Js’in standart directive’leri olduğu gibi sizler de directive yaratabilirsiniz. Örneğin bir yazı yazacaksınız “v-color” dediğinizde yazıların renginin değişmesi ya da “v-font” dediğinizde yazıların özelliklerinin değişmesi gibi etkileşimler yaratabiliriz. Bu işlemi yapmanın iki yöntemi var. Ancak yöntemlere geçmeden önce directive’lerin çalışma mantığını anlamalıyız.

Directive’lerin çalışması için beş farklı metoda yani directive hooks’lara ihtiyaç duyulur. Bunları directive’lerin yaşam döngüsü olarak da düşünebilirsiniz.
bind hook (el,binding,vnode) : En fazla kullanacağımız method budur. Bu hook directive elemente eklendiği anda çalışır. Yani siz elemente bir directive tanımı yaptığınızda ve uygulama çalıştığında bu method çalışır. Üç parametresi bulunur; “el” directive’in kullanıldığı elementi bize verir. “binding” directive’in hangi argümanı aldığını yada ne tür verilerin gönderildiği gibi verileri bize veren parametredir. “vnode” sanal DOM üzerindeki (Virtual DOM) nodu’u bize verir.
inserted hook (el,binding,vnode): Element DOM’a eklenir eklenmez çalışan hook’dur. Örnek olarak style işlemlerinde kullanılabilir.
update hook (el,binding,vnode,oldVnode): Directive bind olduktan sonra update olursa yani güncellenirse çalışır.

Ancak burada bir ayrıntı var; sizin directive’e eklemiş olduğunuz elementin altında başka elementler varsa yada component’ler varsa ve bu component ya da elementlerde güncelleme oluyorsa bu method tetiklenmez ve çalışmaz.

Diğerlerinden farklı olarak 4 parametre içerisine barındırır. “oldVnode” eski update olmamış virtual nodu’u bize veriyor.
componentUpdated hook (el,binding,vnode,oldVnode): Update ile birebir aynı çalışıyor fakat, burada tek fark az önce belirttiğim alt tarafta yani child component varsa tetiklenmeme durumu burada yaşanmaz.
unbind hook (el,binding,vnode) : Directive un bind olduğunda çalışan method’dur.

1. Yöntem

Main.js altında global olarak tanımlamak. Vue.directive(“”,””) dediğiniz anda aslında siz directive oluşturmaya başlıyorsunuz. Directive içerisine iki parametre alıyor. Birincisi directive adınız ve ikincisi de directive’lerin tanımlandığı hook fonksiyonları oluyor.

Header.vue
-----------------
<template>
<div>
<header-main></header-main>
</div>
</template>
...
Main.vue
-----------------
<template>
<div v-renk>
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Optio dignissimos illo maiores est possimus sed magnam ipsum esse porro ipsam? Nesciunt mollitia ipsam fugit ratione sed exercitationem obcaecati cupiditate aut!
</div>
</template>
main.js
-----------------
import Vue from "vue";
import App from "./App";
Vue.directive("renk", {
bind(el, binding, vnode) {
el.style.backgroundColor = "#cd0000";
},
});
new Vue({
el: "#app",
render: (h) => h(App),
});

Burada basit bir directive tanımı yaptık. Bind ile yüklenme anında “el” ile elementi elde ettik ve javascript kod yazarak rengini değiştirdik. Bu işlemi yaparken aynı zamanda directive’lere değer de gönderebiliriz.

Header.vue
-----------------
... (Yukarıda bulunan kodun aynısı)
Main.vue
-----------------
<template>
<div v-renk="'orange'">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Optio dignissimos illo maiores est possimus sed magnam ipsum esse porro ipsam? Nesciunt mollitia ipsam fugit ratione sed exercitationem obcaecati cupiditate aut!
</div>
</template>
main.js
-----------------
...
Vue.directive("renk", {
bind(el, binding, vnode) {
el.style.backgroundColor = binding.value;
},
});
...

Sizin “Main.vue” da yazdığınız veri eğer string ise main.js de karşılığı string dir. Obje yazarsanız obje, integer yazarsanız integer vb. Bizim bu örnekte ihtiyacımız olan veri string olduğu için ..=”’orange’” şeklinde yazdık. Daha önce de belirttiğim gibi binding bizim faklı tipte veriler almamızı sağlar. Bu nedenle “binding.value” dediğimizde aslında v-renk directive’inde tırnak içinde olan değeri almış oluyoruz. Artık directive’inizi başka elementlere eklediğinizde “orange” yerine farklı renkler yazarak kullanabilirsiniz.

Ek olarak directive’ler üzerinden argüman da gönderebilirsiniz. Az önce verdiğimiz örneğe ek olarak directive üzerinde bir tanım varsa backgroundColor gelsin yoksa textColor gelsin şeklinde tanımlamalar yapabilirsiniz.

Main.vue 
-----------------
<template>
<div>
<div v-renk:background="'orange'">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Optio dignissimos illo maiores est possimus sed magnam ipsum esse porro ipsam? Nesciunt mollitia ipsam fugit ratione sed exercitationem obcaecati cupiditate aut!
</div>
<div v-renk="'red'">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Optio dignissimos illo maiores est possimus sed magnam ipsum esse porro ipsam? Nesciunt mollitia ipsam fugit ratione sed exercitationem obcaecati cupiditate aut!
</div>
</div>
</template>
main.js
-----------------
...
Vue.directive("renk", {
bind(el, binding, vnode) {
if (binding.arg == "background") {
el.style.backgroundColor = binding.value;
} else {
el.style.color = binding.value;
}

},
});
...

Bu argüman gönderimini gerçekleştirmek için “v-renk:” ile sonuna “:” koyarak zenginleştirmiş oluyoruz. Yani artık davranışını değiştirecek bir argüman gönderiyoruz. Bundan sonrası zaten main.js kısmında halloluyor. Daha önce belirttiğimiz gibi “binding” bizim argümanlarımızı, value’larımızı ve modifier’larımızı tutan bir yapı demiştik. Bu sayede biz “binding.arg” yazarak “:” dan sonra gelen argümanı yakalayabilir ve basit bir “if” yapısı ile istediğimiz koşulu sağlayabiliriz.

Normal directive’ler için kullandığınız modifier’ları yarattığınız custom directive’lerde de kullanabiliyorsunuz. Örneğin az önceki örnekte “.delay” modifier’ından yararlanarak, “delay” geldiğinde iki saniye sonra yukarıdaki renk değiştirme kurgusunu çalıştır diyebilirsiniz.

Main.vue 
-----------------
<template>
<div>
<div v-renk:background.delay="'orange'">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Optio dignissimos illo maiores est possimus sed magnam ipsum esse porro ipsam? Nesciunt mollitia ipsam fugit ratione sed exercitationem obcaecati cupiditate aut!
</div>
<div v-renk.delay="'red'">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Optio dignissimos illo maiores est possimus sed magnam ipsum esse porro ipsam? Nesciunt mollitia ipsam fugit ratione sed exercitationem obcaecati cupiditate aut!
</div>
</div>
</template>
main.js
-----------------
...
Vue.directive("renk", {
bind(el, binding, vnode) {
if (binding.modifiers["delay"]) {
setTimeout(function () {
if (binding.arg == "background") {
el.style.backgroundColor = binding.value;
} else {
el.style.color = binding.value;
}
}, 2000);
}
},
});
...

Modifier’lardan makalenin başlarında directive konusunda bahsetmiştim. Hatırlarsanız directive’ler “.” dan sonra gelen modifier’lar yardımıyla özellik kazanabiliyordu. Örneğin bir input’a “@keyup.enter” derseniz, yazı yazmaya başlayıp enter’a bastığınızda onchange olacaktır şeklinde yardımcı modifier’lar kullanabiliyorduk. Burada tek kafanızı karıştıracak durum “delay” modifier’ını neden array içine yazdığımız şeklinde bir soru oluşabilir. Bunun sebebi; Modifier’ları sıralı bir şekilde ekleyebiliyor olmanızdan kaynaklı. Yani “.delay.enter.flash..vb” şeklinde yazabiliyorsunuz. Bu nedenle zincirleme modifier tanımlayabildiğiniz için array olarak modifier’ları yakalıyorsunuz.

2. Yöntem

Directive’leri local olarak tanımlamak. Adı üstünde “local” dediğimiz için bu directive tanımlamaları sayfaların içerisinde gerçekleştiriyoruz.

Main.vue 
-----------------
<template>
<div>
<div v-renk:background.delay="'orange'">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Optio dignissimos illo maiores est possimus sed magnam ipsum esse porro ipsam? Nesciunt mollitia ipsam fugit ratione sed exercitationem obcaecati cupiditate aut!
</div>
<div v-renk.delay="'red'">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Optio dignissimos illo maiores est possimus sed magnam ipsum esse porro ipsam? Nesciunt mollitia ipsam fugit ratione sed exercitationem obcaecati cupiditate aut!
</div>
</div>
</template>
<script>
export default {
directives: {
renk: {
bind(el, binding, vnode) {
if (binding.modifiers["delay"]) {
setTimeout(function () {
if (binding.arg == "background") {
el.style.backgroundColor = binding.value;
} else {
el.style.color = binding.value;
}
}, 2000);
}
},
},
},
};
</script>

Dikkat edecek olursanız, örneğin bir component tanımlayacağınız zaman “Vue.component” yazarak tanımlıyorduk, ancak sayfanın Vue instance’ı içine tanımlayacağımızda “components” şeklinde yazıyorduk. Burada da aynı mantık geçerli; main.js içine global tanımlama yaparken “Vue.directive” şeklinde tanımlıyorduk, ancak burada Main.vue instance’ı içerisinde local tanımlama yaparken “directives” şeklinde tanımlıyoruz. “Directives” içerisine birden fazla tanımlama yapabileceğimiz için obje olarak tanımlıyoruz. “renk” directive ismimizi yazdıktan sonra geri kalan kısım zaten global tanımlamada kullandığımız kodun aynısı.

Routing işlemleri

vue-router ayarları

Single page application dediğimizde aklımıza ilk gelecek konulardan bir tanesi routing işlemleri olacaktır. Single page application; Temel olarak bir sayfa üzerinden kullanıcı navigasyonları ile farklı sayfaların aynı sayfa içerisinde gösterilme işlemi diyebiliriz. React’de routing işlemleri için kullanılan react-router-dom modülü gibi, Vue Js için de kullanılan Vue Router adında bir kütüphane kullanılıyor. İlk olarak terminalden “npm i --save vue-router” yazarak router’ı projemize ekliyoruz. Son olarak “main.js” içerisinde aşağıdaki tanımlamayı yaptıktan sonra router’ı kullanabilirsiniz.

Main.js 
-----------------
import Vue from "vue";
import App from "./App";
import VueRouter from "vue-router";
Vue.use(VueRouter);new Vue({
el: "#app",
render: (h) => h(App),
});

Vue Router sayesinde route yani yönlendirme tanımlamamız için bir array’den yararlanıyoruz. Bu array javascript objesi halinde yönlendirmelerimizi tutuyor. Normalde bu routing işlemlerini main.js üzerinden de yapabiliriz ancak anlamlı olması için “src” altına “routes.js” dosya oluşturuyoruz.

App.vue 
-----------------
<template>
<div>
<router-view></router-view>
</div>
</template>
Home.vue
-----------------
<template>
<div>
Home Sayfası
</div>
</template>
User.vue
-----------------
<template>
<div>
<h1>Kullanici sayfasi</h1>
<p>Merhaba Kullanicilar</p>
</div>
</template>
routes.js
-----------------
import Home from "./components/Home";
import User from "./components/User";
export const routes = [
{ path: "", component: Home },
{ path: "/user", component: User },
];
main.js
-----------------
import Vue from "vue";
import App from "./App";
import VueRouter from "vue-router";
import { routes } from "./routes";
Vue.use(VueRouter);const router = new VueRouter({
routes,
});
new Vue({
el: "#app",
router,
render: (h) => h(App),
});

Burada ilk olarak yaptığımız işlem const olarak “routes” adında bir değişken yarattık. Router işleminin bir array olacağından bahsetmiştim. Bu nedenle bir array objesine eşitledik. Bu objeyi diğerlerinden ayıran özellik; içerisindeki property’ler belirli ifadelere sahip. Burada kullanılan “path” hangi yol gelirse hangi component’in çalışacağının sorusuna cevap veriyor. Karşılığını eğer boş bırakırsanız ne olursa olsun proje başladığı zaman bir component yüklensin demiş oluyorsunuz. “component” yazdığınız değerin karşısına da açılmasını istediğiniz component’in ismini yazıyorsunuz. Biz projemiz ilk çalıştığında yada hiç bir yönlendirme yapmazsak “Home” sayfasına gitsin, “/user” yazarsak kullanıcı sayfasına gitsin şeklinde bir tanımlama yaptık.

Tabi sadece bununla bitmedi. “routes.js” içinde yaptığımız yapıyı tüm projeye tanıtmak için “main.js” içerisinde tanımlama yapmamız gerekiyor. “Vue.use” kodundan sonra router adında tüm yönlendirme işlemlerimizi karşılayacak bir değişken oluşturduk ve “new VueRouter” diyerek yeni bir router objesi oluşturduk. Bu objenin içerisine “routes.js” içinde export ettiğimiz yapıyı gönderdik. Burada objenin içine “routes.js” içinde bulunan yapıyı da yazabilirdik ama düzenli olması için dışarıda ayrı bir yapı oluşturduk. Son olarak aşağıda tanımlama yaparak Vue instance içerisinde yeni oluşturduğumuz “router” yapımızı tanımlıyoruz.

Burada “router” özel bir anahtar kelime. Normalde Ecmascript sayesinde biz “router:router” yazmak yerine “router” şeklinde yazdık. yukarıda bulunan “const router” yerine farklı bir isim vermek isterseniz, aşağıda tanımlamasını farklı yapmanız gerekir. Örneğin; “const yonlendirme” derseniz aşağıda “router:yonlendirme” demek zorundasınız.

const yonlendirme = new VueRouter({
routes,
});

new Vue({
el: ‘#app’,
router:yonlendirme,
render: h => h(App)
})

Router ayarlamaları bu kadar. Yaptığınız işlemlerin sonunda browser’ınızı açtığınızda localhost’un yanında “#” bu işareti görüyorsanız doğru yoldasınız demektir. Tanımlamaları yaptıktan sonra nerede gözükeceğini belirtmemiz gerekiyor. Bunun için genel olarak çatı mimari kullanılır. Bu nedenle “App.vue” içerisine vue-router yardımıyla “router-view” etiketi tanımlıyoruz. Bu etiket sayesinde yukarıdaki link lanı değiştiğinde bizim tanımlamalarımızda bulunan componentler gelecek ve sayfada görünecektir.

Daha önce belierttiğim gibi router tanımlaması yaptığınızda Vue router otomatik olarak adres çubuğuna “#” işareti koyar. Bunun sebebi single page application’nun bir davranışıdır. Burada bulunan “#” bize routing modunu veriyor. Yani ilk olarak “localhost:…” kısmı sizin ana domain adresiniz oluyor. İkinci parametre “#” roiting modunuz ve sonrasında gelen “user,home vb.” kısım yönlendirme tanımınız oluyor. Bu “#” işaret sizi rahatsız ediyorsa ufak bir kod ile kaldırabiliyorsunuz.

main.js
-----------------
...
const router = new VueRouter({
routes,
mode: "history",
});
...

main.js’de routes’dan hemen sonra “mode” adında bir parametremiz var. Bu parametre içerisine iki parametre daha alıyor. Bunun varsayılan çalışma şekli “hash” üzerinden, ancak siz burada “history” tanımlarsanız “#” işareti ortadan kalkar.

Component’ler arası gezinme (Navigation Bar)

Sizde biliyorsunuz ki hiç bir kullanıcı adres çubuğuna gidip adres çubuğuna gitmek istediği sayfanın ismini yazmaz. Her uygulamada mutlaka bir menü alanı bulunur.

App.vue
-----------------
<template>
<div>
<app-header></app-header>
<router-view></router-view>
</div>
</template>
<script>
import Header from "./components/Header.vue";
export default {
components: {
appHeader: Header,
},
};
</script>
Header.vue
-----------------
<template>
<ul>
<router-link to="/" tag="li">
<a>home</a>
</router-link>
<router-link to="/user" tag="li">
<a>user</a>
</router-link>
</ul>
</template>

Menu işlemi için “Header.vue” adında bir dosya oluşturduk ve “App.vue” içerisinde bulunan “router-view” tagının yukarısına tanımladık. Route’ların çalışması için “router-link” adında yeni bir tag’dan yararlandık. Bu tag için single page application’larda route’lar arasında dolaşmamızı sağlayan element diyebiliriz. Birçok özelliği içinde barındırıyor; tıklandığında nereye gideceği, hangi class’a sahip olacağı, hangi kelime geldiğinde hangi işlemi yapacağı, yada hangi elemente sahip olacağı gibi bir çok parametreye sahip.

Yukarıdaki örnekte “tag=li” aslında hangi elemente sahip olacağının bir göstergesi. Burada eğer bunu yazmazsak “ul” menü yapımız bozulacaktı. “tag=li” yazarak aslında günün sonunda DOM oluştuğunda yani render edildiğinde “li” elementine dönüşeceksin demiş oluyoruz. Dikkat ettiyseniz “<a> ” elementinde bulunan “href” kısımlarını kaldırdık. Bunun nedeni single page application’larda “href” ler yardımıyla gezinemiyoruz. Aslında yöntemleri var ancak çok fazla uğraşmanız gerekiyor. Burada “to” ile belirttiğiniz kısmın karşılığı, sizin “routes.js” dosyanızda oluşturduğunuz adreslerle aynı olması gerekiyor. Çünkü o adreslere göre yönlendirme yapıyor.

Konumuz css olmadığı için görsele çok önem vermedim. Ancak ek bir bilgi vermek istiyorum; Menünüzü oluştururken tıklanmış olduğunuz yani bulunduğunuz sayfadaki menünün aktif olarak görünmesini isterseniz “active-class=class-ismim” şeklinde bir tanımlama yapabilirsiniz. “class-ismim” için bir stil yazdığınızda bulunduğunuz sayfanın ilgili linki sizin verdiğiniz stil şeklinde görünecektir.

Header.vue
-----------------
<template>
<ul>
<router-link active-class="class-ismim" to="/" tag="li">
<a>home</a>
</router-link>
<router-link active-class="class-ismim" to="/user" tag="li">
<a>user</a>
</router-link>
</ul>
</template>
<style scoped>
.class-ismim {
border:1px solid red;
}
</style>

Yukarıdaki örnekte active menünün class’ının çalıştığını görebilirsiniz ancak siz de farkedecesinizdir “user” component’imiz çağrıldığında iki menü de aktif görünmektedir.Bunun nedeni; Siz bir linke tıklayıp adres çubuğunu değiştirdiğinizde, aslında “/” işareti ile beraber “user” çağrıldığı ve “home” sayfasını çağırdığınız link’de de “/” bulunduğu için ikisi de çağrılmış gibi algılayıp bu hatayı ortaya çıkartıyor. Bu durumun önüne geçmek için “exact” adında bir property’den yararlanıyoruz. Exact dediğimizde; sadece router linkte bulunan “to” nun karşılığı ne ise yani tamamı denk gelip uyuşuyorsa işte o zaman bizim belirlediğimiz active-class’ı ekle demiş oluyoruz.

Header.vue
-----------------
...
<router-link active-class="class-ismim" exact to="/" tag="li">
<a>home</a>
</router-link>
<router-link active-class="class-ismim" exact to="/user" tag="li">
...

Bazen uygulamamız üzerindeki bir sonuca göre yönlendirme işlemi yapmaya ihtiyaç duyabiliriz. Örneğin bir banka uygulamasında belli bir süre dokunmadığınızda sizi şifre sayfasına yönlendirmesi gibi.

User.vue
-----------------
<template>
<div>
<h1>Kullanici sayfasi</h1>
<p>Merhaba Kullanicilar</p>
<button @click="goHome">Home sayfasına git</button>
</div>
</template>
<script>
export default {
methods: {
goHome() {
this.$router.push("/"); //1. yontem
//this.$router.push({ path: "/" }); //2. yontem
},
},
};
</script>

User sayfamızda “p” nin altına yönlendirmek için bir buton oluşturduk. Bu butonun fonksiyonunda “this.$router” şeklinde bir tanımlama yaptık. Bu tanımlama daha önce de belirttiğim gibi bizim router ile bağlantı kurmamızı sağlıyor. Ek olarak kullandığımız “.push(“/”)” methodu bizim herhangi bir route içerisinde tanımladığımız yollardan birine yönlenmemizi sağlıyor. Bu işlemi gerçekleştirirken üç farklı yol kullanıyor. Birinci yöntem az önce verdiğim örnek gibi “.push(“/”)” kullanılarak yönlendirme yöntemi. İkinci yöntem ise “push” içerisine bir obje alır. Bu obje bizim router tanımını yaparken kullanmış olduğumuz “path” adını alır. Üçüncü yöntem ise yine bir obje alır ancak bu sefer isimlendirilmiş bir route ile yönlendirme alır. Bunu kullanabilmeniz için “routes.js” dosyanızda bulunan yönlendirme objelerine “name” eklemeniz yeterli olacaktır. Bu tür yönlendirmelere “isimlendirilmiş route’lar” deniyor.

routes.js
-----------------
import Home from "./components/Home";
import User from "./components/User";
export const routes = [
{path: '', component: Home, name: "anasayfa"},
{path: '/user', component: User, name: "kullanici"}
]
</script>
User.vue
-----------------
...
<script>
export default {
methods: {
goHome() {
this.$router.push({ name: "anasayfa" }); //3. yontem
},
},
};
</script>

Route ile Parametre Gönderimi

Şuana kadar component’ler arasında gezinirken tamamiyle statik bir route yöntemi üzerinden ilerledik. Yani “user” ise “user” sayfası çağrıldı. Ancak bizden user’a tıkladığımız zaman hangi user’a tıkladığımızın bilgisi yada hangi user’ı çağırmak istiyorsam onun bilgilerini ekrana dökmemiz istenebilir. Bu gibi durumlarda route’a parametre göndermeniz gerekir.

User.vue
-----------------
...
export const routes = [
{ path: "", component: Home, name: "anasayfa" },
{ path: "/user/:id", component: User, name: "kullanici" },
];

User route’undan sonra içerisine bir parametre alır. Bu parametreyi “/:id” ile belirtiyoruz. Bunun anlamı; “user/” dan sonra siz ne yazarsanız yazın o sizin gönderdiğiniz parametre oluyor. Bu şekilde bir parametre gönderdiğinizde aşağıdaki şekilde parametreyi yakalayıp kullanabiliyorsunuz.

User.vue
-----------------
<template>
<div>
...
<p>parametreyi yakaladık : {{ id }}</p>
...
</div>
</template>
<script>
export default {
data() {
return {
id: this.$route.params.id,
};
},
...
};
</script>

Burada “p” tagı içinde bir interpolution oluşturduk ve “id” tanımladık. Tabi ki bunu okuyabilmemiz için data içinde yine bu id’yi tanımlayıp “this.$route.params.id” şeklinde bir javascript ile parametreyi yakalıyoruz. Burada dikkat ederseniz “$router” şeklinde yazmadık. Yazımları çok yakın olduğu için karıştırılmasın; “$router” bizim bağlantı kurmamızı “$route” ise bizim “router.js” içerisinde tanımladığımız verileri yakalamamızı sağlayan bir parametredir. Burada yazdığımız javascript kodu ile “$route altında bulunan parametrelere git ve bize id’yi ver” demiş oluyoruz.

Yukarıda yaptığımız örneklerdeki gibi tek bir parametre özelinde çalışıyorsanız sorun yaşamayacaksınız. Ancak bir component içinde birden fazla parametre ile işlem yapmak istiyorsanız ufak bir sorunla karşılaşırsınız.

User.vue
-----------------
<template>
<ul>
<router-link to="/" tag="li"><a>home</a></router-link>
<router-link to="/user/1" tag="li"><a>user 1</a></router-link>
<router-link to="/user/2" tag="li"><a>user 2</a></router-link>
</ul>
</template>

};
</script>

Bunun sebebi; User component’imizin çoktan yaratılmış olması, yani içinde bulunan “data()” metodunun çoktan çalışmış ve id değerinin verilmiş olmasıdır. Bunun çözümü için Vue Js’in daha önce de gördüğümüz reactivity yöntemlerinden watch metodundan yararlanacağız.

User.vue
-----------------
...
<script>
export default {
data() {
return {
id: this.$route.params.id,
};
},
watch: {
$route(to, from) {
this.id = to.params.id;
},
},

};
</script>

Watch hatırlarsanız bir property’i izleyebiliyorduk. Yani değeri değiştiği anda aksiyon almamızı sağlıyordu. Burada değeri değişen “$route” paramatresi olduğu için watch içerisinde tanımlayacağız. Burada kullanılan “to” daha önce de bahsettiğimiz “value” ya denk geliyor, ayni burada bizim değişen route’umuzu veriyor. Kafanız karıştıysa burada “to” yerine “value” da yazabilirsiniz. “From” ise bizim için yeni bir kavram, aslında eski değer(oldValue) anlamına gelmektedir, yani değişmeden önceki halini bize vermektedir. Burada yazdığımız kod “bana route içindeki to’nun parametre’lerinden id’yi getir ve data’nın içinde bulunan id’ye eşitle” yi ifade etmektedir.

Query ile parametre gönderimi

Web sayfalarımızda sıklıkla yaptığımız bir konu daha var. Hangi teknolojiyi kullanırsanız kullanın url kullanılarak paremetreler yardımıyla veri gönderimi yapıyoruz. Bunun en çok kullanılan yöntemi hepinizin bildiği gibi “…user/1?name=yavuz&surname=akıncı” bu ve bunun gibi şekillerde “?” sonrasında veriler göndererek sağlıyoruz. Bunu iki şekilde yapabilirsiniz;

Header.vue (1.yöntem)
-----------------
<template>
<ul>
<router-link to="/" tag="li"><a>home</a></router-link>
<router-link to="/user/1?name=yavuz&surname=akıncı" tag="li"><a>user 1</a></router-link>
</ul>
</template>

Statik bir şekilde hiç güzel görünmeyen bir şekilde tanımlama yapmak. Yada “:to” kısmını v-bind ile dinamik hale getirmek ve bir obje içerisinde parametre göndermek.

Header.vue (2.yöntem)
-----------------
<template>
<ul>
<router-link to="/" tag="li"><a>home</a></router-link>
<router-link
tag="li"
:to="{
path: '/user/1',
query: {
name: 'yavuz',
surname: 'akıncı',
}

}">
<a>user 1</a>
</router-link>
</ul>
</template>

Yukarıdaki gönderimi hatırlarsanız “.push()” konusunu anlatırken ikinci yönlendirme yöntemi olarak anlatmıştım. Burada “path” parametresi ile yönlendirme yapacağınız yeri belirtebileceğiniz gibi “query” adında bir router özelliği ile parametre gönderimi de yapabilirsiniz. Burada birden fazla parametre gönderebileceğiniz için “query” tahmin edebileceğiniz gibi bir obje içine alıyor. Bu gönderdiğiniz verileri kullanmak isterseniz yönleneceğiniz sayfada aynı “$route.params” şeklinde id’ye ulaşabildiğiniz gibi aynı şekilde “$route.query” yazarak route üzerinden gönderilen parametrelere ulaşabilirsiniz.

User.vue
-----------------
<template>
<div>
<h1>Kullanici sayfasi</h1>
<p>Merhaba Kullanicilar</p>
<p>Adım : {{ $route.query.name }}</p>
<p>Soyadım : {{ $route.query.surname }}</p>
</div>
</template>

Burada unutmayın; hatırlarsanız bu kullanım sadece tek sayfa üzerinden geçerli. Eğer burada user sayfamızdan iki tane olsaydı bir önceki konuda gösterdiğim gibi watch üzerinden route’u takip etmeli ve yapıyı onun üzerinden ilerletmelisiniz.

Redirect işlemleri

Redirect veya yönlendirme, web siteniz yada uygulamanız içerisinde verilen bir komut vasıtasıyla bir adresin farklı bir adrese geçici veya kalıcı olarak yönlendirilmesine denir. Daha önceden belirlediğimiz linklere yönlendirme işlemini gerçekleştirdik. Ancak belirlemediğimiz bir link üzerinden ne gibi işlemlerin yapılacağını tanımlamadık.

routes.js
-----------------
import Home from "./components/Home";
import User from "./components/User";
export const routes = [
{ path: "", component: Home, name: "anasayfa" },
{ path: "/user/:id", component: User, name: "kullanici" },
{ path: "/404", redirect: "/" }
];

Örneğin mevcut Home ve User sayfalarımız haricinde adres çubuğumuza “404” şeklinde bir ifade yazarsak bizi Home sayfasına yönlendirsin yani redirect işlemi yapsın diyebilirsiniz. Burada string yazmak zorunda değilsiniz. Redirect yaparken aynı “query” de olduğu gibi obje de gönderebilirsiniz.

Tabi bu durum aslında tam olarak dinamik bir yapı değil aslında bizim global kod oluşturabilmemiz için tüm yanlış yazımlara karşı yönlendirme yapmamız daha mantıklı olacaktır.

routes.js
-----------------
...
export const routes = [
{ path: "", component: Home, name: "anasayfa" },
{ path: "/user/:id", component: User, name: "kullanici" },
{ path: "*", redirect: "/" }
];

Bu kullanım şekline wildcard denilmektedir.

Router’lar için Lazy Load

Bu zamana kadar yaptığımız örneklerde dikkat ettiyseniz tüm component’leri tek bir route.js içinde tanımladık ve projemize import ettik. Yani component’lerinizi kullansanız da kullanmasanız da tüm doslarınız tek bir build.js içerisinde webpack tarafından derlenip yüklendiği için bütün componentler yüklenmiş oluyor. Bu konu herhangi küçük bir uygulamada aslında çok önemli bir durum gibi gözükmese de, çok büyük uygulamalarda bu bize problem oluşturabilecek bir durum. Webpack büyük projelerde perforformansı arttırmak için lazy load adında bir özellik bizlere sunuyor. Lazy load hangi component’i hangi zamanda kullanırsanız o zamn yüklemenize fırsat sunuyor.

Bu kurguyu anlatabilmek adına örnek olarak projemizde component klasorünün altına beş adet örnek component oluşturdum. Home,user,about,contact ve maps adında ve hepsinin içinde “merhaba … sayfası” yazmakta. (hepsinin içini buraya kopyalamamak adına bu gereksiz bilgiyi veriyorum :)) Header.vue dosyamızda routing yollarını ve router.js dosyasında routing yapılarını oluşturdum.

Header.vue
-----------------
<template>
<ul>
<router-link to="/" tag="li"><a>home</a></router-link>
<router-link to="/user" tag="li"><a>user</a></router-link>
<router-link to="/about" tag="li"><a>about</a></router-link>
<router-link to="/contact" tag="li"><a>contact</a></router-link>
<router-link to="/maps" tag="li"><a>maps</a></router-link>
</ul>
</template>
routes.js
-----------------
import Home from "./components/Home";
import User from "./components/User";
import About from "./components/About";
import Contact from "./components/Contact";
import Maps from "./components/Maps";
export const routes = [
{ path: "", component: Home, name: "anasayfa" },
{ path: "/user", component: User, name: "kullanici" },
{ path: "/about", component: About, name: "hakkimizda" },
{ path: "/contact", component: Contact, name: "iletişim" },
{ path: "/maps", component: Maps, name: "haritalar" },
];

Yukarıdaki görselde dikkat ederseniz projenizi çalıştırdığınız anda 2MB’lik build.js dosyası gelmekte. Yani projenizdeki tüm component’ler içlerinde yapılan işlemler yada verilerin kısacası herşeyin sayfamızda performans maliyeti 2MB (Dediğim gibi bu sadece büyük projeler için. Normalde büyük projelerde bu dosya çok daha yüksek çıkacaktır).

Performans konusunda dokunmamız gereken nokta routes.js. Çünkü tüm component’leri bu dosya içerisinde aşağıda bulunan routes alanına denk gelse de gelmese de yüklemiş oluyoruz.

routes.js
-----------------
import Home from "./components/Home";
import User from "./components/User";
const About = (resolve) => {
require.ensure(["./components/About"], () => {
resolve(require("./components/About"));
});
};
const Contact = (resolve) => {
require.ensure(["./components/Contact"], () => {
resolve(require("./components/Contact"));
});
};
const Maps = (resolve) => {
require.ensure(["./components/Maps"], () => {
resolve(require("./components/Maps"));
});
};
export const routes = [
{ path: "", component: Home, name: "anasayfa" },
{ path: "/user", component: User, name: "kullanici" },
{ path: "/about", component: About, name: "hakkimizda" },
{ path: "/contact", component: Contact, name: "iletişim" },
{ path: "/maps", component: Maps, name: "haritalar" },
];

Yazım şekli size biraz garip gelebilir ancak webpack ancak bu yazım şekliyle algılıyor. Yukarıda yüklenme farklarını görebilmeniz için about,contact ve maps sayfalarını performans kurgusuna dahil ettim siz tüm sayfalarınızı bu şekilde ayrıştırabilirsiniz. Burada yapıyı anlayacak olursak; Sayfa component’inizin adınızı yazıyorsunuz ve “resolve” adında bir değişken atıyorsunuz. “Resolve” içerisinde “require” adında bir fonksiyon tanımlıyorsunuz. “Require” kendi içinde “ensure” adında bir fonksiyon kullanıyor. Bu fonksiyon içerisisine iki adet parametre alıyor. Birincisi sizin yüklemek istediğiniz component’inzin yolu, ikincisi ise bir fonksiyon.Bu fonksiyonun görevi, sanki bir yere istek atmışız ve bize bir cevap dönmüş gibi tekrar bize bir “resolve” işlemi döndürüyor. Bu dönüş işleminin içine tekrar yüklemek istediğimiz component’i yazıyoruz. Bu içine yazdığımız “require(“./components/Maps”)” yapının aslında yukarıda tanımladığınız “import Maps from “./components/User”” kısmından bir farkı yok. Bu “require” yapısını yukarı yazsanız yine aynı şekilde çağırmış olurdunuz. Bu noktada webpack “ensure içine yazdığım component kullanılacaksa bunu benim require ile kesinlikle yüklemem gerekiyor” diyor. Bu sayede bize ihtiyacımız olan yapıyı küçük küçük teslim etmiş oluyor.

Bu yapıya göre ilk olarak “home ve user” sayfalarına tıkladığımda hiç bir değişiklik olmadı ve sadece “build.js” dosyası yüklendi. Ardından “about,contant ve maps” sayfalarına tıkladığımızda, her component yüklenişinde o sayfalarla ilgili build dosyaları teker teker yüklendi. Gerçekten güzel bir özellik ancak dediğim gibi burda boyutlar çok küçük olduğu için belki farkı anlamamış olabilirsiniz. Büyük projelerde çok işinize yarayacaktır. Ek olarak bu yapınızı gruplama imkanınız da var örneğin “kullanıcı sayfam yüklendikten sonra bu sayfalar yüklensin” şeklinde.

routes.js
-----------------
...
const About = (resolve) => {
require.ensure(["./components/About"], () => {
resolve(require("./components/About"));
},"User");
};
const Contact = (resolve) => {
require.ensure(["./components/Contact"], () => {
resolve(require("./components/Contact"));
},"User");
};
const Maps = (resolve) => {
require.ensure(["./components/Maps"], () => {
resolve(require("./components/Maps"));
},"User");
};
...

Kapanış

Gerçekten uzun bir aradan sonra bu makaleyi hazırlama fırsatı buldum. Tam anlamıyla Vue Js’de uzmanlaşmam ,doğru bilgiyi sizler için toparlamam ve projelerden kalan vakit içinde makale oluşturmak zor oldu. Bu makale 69 sayfa ve 12811 kelime ile React,Redux ve Angular makalelerimden çok daha uzun oldu. Çevremdeki insanlar “makalelerin çok uzun parçalara ayır” diye ısrar etseler de, benim amacım sayfa içinde “CTRL+F” ye bastığınızda bir el kitabı gibi işinizi yaparken takıldığınız noktada aratıp kullanmanız. React makalemde de olduğu gibi Vuex konusunu ayrı işleyeceğim. Daha önce de belirttiğim gibi bu component’ler arası iletişim props gönderimi sadece Eventbus’lar yada düz propslar üzerinden yapılmıyor. Aynı Redux’da olduğu gibi Vuex üzerinden bu işlemi yapabiliyorsunuz. Bu sene içinde Vuex ile alakalı bir makale yayınlayıp Vue Js konusunu bitirmeyi planlıyorum. Başka makalelerde görüşmek üzere umarım meslek hayatınızda bu makalelerin faydasını görüyorsunuzdur. Elimden geldiğince her konuya değinmeye çalıştım. Oluşturduğunuz en ufak projede bile benim verdiğim bilgiden yararlanıyorsanız ne mutlu bana.

PART : 2 Vuex ve State yönetimi çok yakında….

--

--