VUE.JS

Vue.js Routing #1

Güncelleme:
Vue.js giriş yazısında Vue’nun temel kapsamının ve özelliklerinin resmi ve/ya 3. parti geliştiriciler tarafından sağlanan eklentiler vasıtasıyla genişletilebileceğinden bahsetmiştim. Hatta, bu eklentilerin yer aldığı Awesome ...
GÖRSEL
Vue.js giriş yazısında Vue’nun temel kapsamının ve özelliklerinin resmi ve/ya 3. parti geliştiriciler tarafından sağlanan eklentiler vasıtasıyla genişletilebileceğinden bahsetmiştim. Hatta, bu eklentilerin yer aldığı Awesome Vue.js listesinin de güncel olarak takip edilebileceğini belirtmiştim. İki bölüm halinde, bahsi geçen resmi eklentilerden biri olan Vue-router‘dan bahsedeceğim. İlk yazı sıklıkla kullanacağımız temel özellikleri içermekte.

Vue.js Router

Vue-router, Vue.js için duyurulmuş resmi router eklentisidir ve Vue.js ile Tek Sayfa Uygulamaları (Single Page Applications) geliştirebilmek amacıyla Vue.js çekirdeği ile bütünleşir. Şu özellikleri barındırır:
  • Nested route/view mapping
  • Modüler, bileşen temelli (component-based) router yapılandırması
  • Route parametreleri, sorguları, joker karakter desteği
  • View transition özellikleri
  • Fine-grained navigasyon kontrolü
  • Otomatik aktif CSS sınıflarına sahip bağlantılar
  • HTML5 history mode ya da hash mode
  • Özelleştirilebilir Scroll davranışları
Nested routing ile ilgili de bir not eklemekte fayda var. Klasik bir GET request (istek) “scheme://domain:port/path?query_string” şeklindedir. URI, URL ve URN başlıklı yazıda bu konuya istinaden bazı notlar aktarmıştım. /pageId=1&commentId=10 şeklinde bir URL örneği oluşturalım. RESTful API geliştirilirken Endpoint (URL) olarak /pages/1/comments kullanımını tercih ederiz. Bu örnekte Page ID’si 1 olan yazının tüm yorumlarını edinebiliriz. Spesifik bir yorum için işlem yapmak isteyelim ve URL’e şu eklemeyi yapalım; /pages/1/comments/10. Bu durumda ilgili yazının 10 numaralı yorumuna işlem yapabiliriz. Routing (URL Eşleştirme / Yönlendirme) başlıklı yazıda verilen örneklere ulaşabilirsiniz. Node.js, Django, Rails, Laravel gibi Full-Stack Framework’lerde bu işlemler Resources olarak nitelendirilir. Tekrar örneğe dönecek olursak, /pages/1/comments/10 yapısında pages route içerisinde comments route bulunması route işlemini dinamik (dynamic route maching) hale getirir. Ancak, comments yerine replies ya da shares gibi bir bileşeni de dahil etmek istersek? İşte bu kullanım bir nested route olarak ifade edilebilir ve RESTful API ve web uygulamalarında kolaylıkla kullanılabilir. Detayları aktarmaya örnekler üzerinden devam edeceğim. Şimdi, bir sonraki konuya geçebiliriz.

Vue.js Routing Kurulumu

Doğrudan veya Vue-CLI aracılığıyla eklentiyi çalışmalarımıza dahil edebiliriz.
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
Ya da vue add komutu ile edinebiliriz.
vue add router
Ayrıca kullandığımız paket yöneticileri (örneğin yarn) aracılığıyla da vue-router’ı çalışma alanımıza dahil edebiliriz.
npm install vue-router
#YA DA
yarn add vue-router
Routing (URL Eşleştirme / Yönlendirme) Nedir? başlıklı yazıda basit bir Vue.js Routing örneği yapmıştık. Vue.js tarafından yayınlanan Simple Routing From Scratch de ayrıca incelenebilir.

Vue Router İşlemi

Vue-router kullanımında linklendirmeleri HTML etiketleri yerine biçiminde declarative olarak tanımlarız. Vue, render aşamasında ilgili link tanımını (router link) bizim için HTML etiketlerine dönüştürür. Ayrıca, programmatic olarak da linklendirme işlemi yapabilmekteyiz. Bu amaçla kullanacağımız fonksiyonumuz ise router.push(...) şeklinde kullanılmaktadır.

Dinamik olarak verileri bize iletecek View alanımızı ise etiketleriyle belirtiriz. Vue-link ile değişen URL için bu etiket cevap verir.

Toparlayacak olursak, vue-router en temelinde şu parçalardan oluşur:

<router-link to="/foo">foo</router-link>
<router-link to="/bar">bar</router-link>
 
<router-view></router-view>

Dinamik Route Eşleştirme

Şablonlar aracılığıyla route işlemini aynı bileşene eşleyerek ilerleyebiliriz. Örneğin, az önce nested routing açıklamasında pages ve comments’ten bahsetmiştim. Bu bağlamda pages ve comments adında bileşenlerimiz olduğunu düşünelim. Ancak, her page ve her comment birbirinden bağımsız. Dolayısıyla, vue-router ile dinamik bir şekilde eşleşen verileri görüntülemek isteyebiliriz:
const Page = {
  template: '<div>Page</div>'
}
 
const router = new VueRouter({
  routes: [
    { path: '/pages/:id', component: Page }
  ]
})
Bu şekilde URL /pages/foo ve /pages/bar olduğunda benzer route’a map’lenecektir. :id tanımı dinamik bir segmenti ifade eder. Bu dinamik segmentler : ile ifade edilir. Böylelikle, route eşleştiğinde dinamik segmentin değeri (value) her bileşen için this.$route.params ile gösterilir.
const Page = {
  template: '<div>Page {{ $route.params.id }}</div>'
}
Bu anlattıklarımızı bir örnek olarak toparlayalım:
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
 
  <section id="app" class="section" v-cloak>
    <div class="container">
      <div class="navbar-menu">
        <router-link to="/pages/foo" class="navbar-item">Page foo</router-link>
        <router-link to="/pages/bar" class="navbar-item">Page bar</router-link>
      </div>
      <hr />
      <router-view></router-view>
    </div>
  </section>
 
  <script>
    const Page = {
      template: `<p class="title">It is a {{ $route.params.id }} page</p>`
    }
 
    const router = new VueRouter({
      routes: [{
        path: '/pages/:id',
        component: Page
      }]
    })
 
    const app = new Vue({
      router
    }).$mount('#app')
  </script>
Tekrar örnek ifadeye dönelim; /pages/1/comments/10. Bu ifadedeki /pages/:id işlemini başarıyla eşleştirdik. Peki, comments gibi çoklu dinamik segmentleme işleminde nasıl ilerlememiz gerekir? Cevap, $route.params.
şabloneşleşen yol$route.params
/pages/:pageId/pages/1{ pageId: 1 }
/pages/:pageId/comments/:commentId/pages/1/comments/10{ pageId: 1, commentId: 10 }
$route.params dışında $route nesnesi ile de $route.query (eğer URL bir query içeriyor ise) ve $route.hash gibi pek çok detaya erişebilmekteyiz.

Parametre Değişiklikleri

Yukarıdaki örnekte /pages/foo ve /pages/bar değişimlerinde aynı component cevap vermekte. Bu, her iki route’un da aynı bileşeni render etmesi eski instance’i kalırıp ve yeni instance’i oluşturmaktan daha etkili bir yöntemdir. Bu aynı zamanda, route ile işlem gören component’lerin lifecycle hooks dışında kalabileceği anlamına da gelmekte. Bu tür durumlarda $route nesnesi watch edilebilir ya da beforeRouteUpdate kullanılabilir.
const Page = {
  template: '...',
  watch: {
    '$route' (to, from) {
      // route değişikliklerine karşı uygulanacak işlemler
    }
  }
}
const Page = {
  template: '...',
  beforeRouteUpdate (to, from, next) {
    // route değişikliklerine karşı uygulanacak işlemler
    // next()'i call etmek unutulmamalı!
  }
}

Tüm Kapsam / 404 Not found Route

Düzenli ifadeler / parametreler sadece tanımlandıkları ve / ile ayrıştırıldıkları şekilde eşleşirler. Bunun yanı sıra, vue-router asterisk (*) kullanımını da desteklemektedir.
{
  // her değerler eşleşir
  path: '*'
}
{
  // `/page-` ile başlayan değerlerle eşleşir
  path: '/page-*'
}
Asterisk (*) kullanımında parametre doğrudan $route.params‘a da iletilir. İletilen bu değer pathMatch ile edinilebilir.
// Route { path: '/page-*' }
this.$router.push('/page-about')
this.$route.params.pathMatch // 'about'
 
// Route { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'
Bu özellikle birlikte, 404 Not found gibi hata sayfaları için asterisk (*) kullanımı değerlendirilebilir. Asterisk dışında RegExp ile daha kapsamlı çözümler de üretilebilir.

Nested Routes

Nested routing ile ilgili açıklamaya yazının ilk bölümlerinde yer vermiştim. Uygulamaların çoğu zaman pek çok bileşenin iç içe ve bir araya gelmesinden oluştuğunu ve bir URL’in birden fazla segment barındırabildiğini biliyoruz. Bu segmentlerin : ile ifade edildiğinden bahsetmiştim. Şimdi konuyu biraz daha detaylandırıp örneklendirelim ve şu şekilde URL yapıları oluşturalım:
  • pages/foo/comments
  • pages/foo/shares
  • pages/foo/replies
Bu örnekte Pages Comments’i, Shares’i ve Replies’i ayrı kapsamaktadır. Comments, Shares ve Replies ise birbirinden bağımsızdır. O halde, yukarıda oluşturduğumuz örneğimizi bu yapıya uygun şekilde güncelleyelim:
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
 
  <section id="app" class="section" v-cloak>
    <div class="container">
      <div class="navbar-menu">
        <router-link to="/pages/1/comments/11" class="navbar-item">Comments</router-link>
        <router-link to="/pages/2/replies/22" class="navbar-item">Replies</router-link>
        <router-link to="/pages/3/shares/33" class="navbar-item">Shares</router-link>
      </div>
      <router-view></router-view>
    </div>
  </section>
 
  <script>
    const Page = {
      template: `
        <div class="user">
          <h2>Params: {{ $route.params }}</h2>
          <router-view></router-view>
        </div>
      `
    }
 
    const userComments = {
      template: `<div>Subpage: Comments</div>`
    }
    const userShares = {
      template: `<div>Subpage: Sharings</div>`
    }
    const userReplies = {
      template: `<div>Replies</div>`
    }
 
    const router = new VueRouter({
      routes: [{
        path: '/pages/:pageId/:pageParts/:partId/',
        component: Page,
        children: [{
            path: 'comments',
            component: userComments
          },
          {
            path: 'shares',
            component: userShares
          },
          {
            path: 'replies',
            component: userReplies
          }
        ]
      }]
    })
 
    const app = new Vue({
      router
    }).$mount('#app')
  </script>
Evet, menüde yer alan linkleri kullanarak parametreleri görüntüleyebilir, child compotent’ler ile uygulama yapısını daha kapsamlı bir hale getirebiliriz.

HTML5 History Mode

Bu yazı kapsamında son bahsedeceğim konu HTML5 Geçmiş Modu (History Mode). Yukarıdaki örneklerde de görüldüğü üzere vue-router ön tanımlı olarak hash (#) modu kullanmaktadır. Bu sayede, URL’i sembolize eden hash ile sayfa yenilenmeden URL değişiklikleri uygulanabilmekte. Çeşitli gereksinimler doğrultusunda hash mode yerine history mode kullanmamız gerekebilir. Bu durumda mod değişikliğini router’a belirtmemiz gerekir.
const router = new VueRouter({
  mode: 'history',
  routes: [...]
})
History mode’un aktifleştirilmesinin ardından URL’lerimiz daha “normal” görünecektir. Ancak, history mode ile ilgili unutmamamız gereken bir konu var. SPA uygulamalarda bir server yapılandırması söz konusu değilse history mode 404 hatasına neden olur. Apache için örnek yapılandırma:
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>
Nginx için örnek yapılandırma:
location / {
  try_files $uri $uri/ /index.html;
}
Firebase hosting için örnek yapılandırma:
{
  "hosting": {
    "public": "dist",
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}
Son olarak, server yapılandırması neticesinde artık eşleşmeyen durumlar için 404 hatası oluşturamayabilirsiniz. Çünkü, her durumda uygulama bir sayfa üzerinden URL eşleştirmesi sağlayacaktır, ancak server bir sayfa hatası almayacaktır. Bu gibi durumlarda yazı içerisinde de bahsi geçen asterisk (*) kullanımını tercih edebilir ve eşleşmeyen URL’ler için hata içeriği döndürebiliriz.
const router = new VueRouter({
  mode: 'history',
  routes: [
    { path: '*', component: NotFoundComponent }
  ]
})
Evet, vur-router ile ilgili sıklıkla karşılaştığım durumlardan bahsetmiş olduk. Bir sonraki yazıda biraz daha teknik ayrıntılara değinece ve sonrasında vue-router ile örnek bir uygulama hazırlayacağız. Öncesinde, vue-router ile ilgili daha detaylı bilgi almak isterseniz ilgili Vue kaynakçasına göz atabilirsiniz.

HABERDAR OL

Yeni eklenen projeler, eğitimler, içerikler ve yayınlanan videolar e-posta adresine gelsin.