Vue.js (v2.x) Single-File JavaScript Components

Template, CommonJS, Sunucu Ayarları, ...

Vue.js‘e dair ilk yazıda, Vue.js özelliklerini herhangi bir ek gereksinim olmaksızın (full builds; vue.js1 2; compiler ve runtime‘i3 4 içerir) nasıl kullanabileceğimizden bahsetmiştim.

AA

Ardından, Vue.js temel özellikleri ve klasik kullanımı ile pek çok detaya ve örneğe değinmeye çalıştım. Bir önceki yazıda (ve sonraki anlatımlarda temel olarak ele alınacak olan) ise NPM ve CLI kullanımı ile diğer yapılandırma süreçlerine geçmiş olduk. Bu yazıda ise .vue uzantılı dosyaları nasıl kullanabileceğimizden bahsedeceğim.

Vue.js Single-File JavaScript Bileşenleri

Vue projelerinde global component’leri Vue.component ile tanımlamakta ve taşıyıcı (container) ögesini (element) new Vue({ el: '#container' }) ile işaretlemekteyiz. Şimdiye değin Vue işlemlerini bir HTML ve JavaScript dosyaları (kimi zaman aynı dosyada) ile gerçekleştirdiğimizi hatırlarsınız. Bu örneklerde component’leri (bileşenler) de aynı dosya içerisinde bulundurmaktaydık. Bu süreç basit uygulamalar geliştirirken pek sorun yaratmasada, söz konusu kapsamlı çalışmalar olduğunda kod ve içerik yönetiminde karmaşa oluşmasına neden olmakta. Bu sorunlardan bazılarını şu şekilde özetleyebiliriz;

  • Global definitions; her component için benzersiz bir isim kullanmamız gerekir,
  • String templates; syntax highlighting (söz dizimi vurgulama) ve okunaksızlaşan HTML içeriği
  • No CSS support; HTML ve JS içeriği modüler bir şekilde yönetilebilirken, CSS akışının component tanımı dışından yönetilmesi
  • No build step; Pug veya Babel gibi önişlemciler (preprocessors) yerine HTML ve ES5 JavaScript ile sınırlandırması

Uygulamanın kapsamı genişledikçe yukarıda, maddeler halinde ifade edilen sorunlar oldukça can sıkıcı hale gelecektir. İşte, bu gibi durumlara çözüm olarak single-file component kullanımı sunulmakta. Bu kullanım için *.vue uzantılı dosyalar kullanılmakta ve bu dosyalar Webpack veya Browserify gibi araçlarla yönetilebilmekte. Hemen basit bir örnekle şimdiye kadar bahsettiğimiz tüm konuları toparlayalım:

<template>
  <p>{{ message }}</p>
</template>

<script>
module.exports = {
  data: function () {
    return {
      message: 'Hello World!'
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

Oldukça basit bir şekilde oluşturduğumuz dosyamızı hello.vue adıyla kayıt edelim. Görüldüğü üzere, aynı dosya içerisinde, birbiriyle ilişkili 3 bölüm yer alıyor:

<template>...</template>
<script>...</script>
<style>...</style>

// ya da

<template>...</template>
<script src="..."></script>
<style src="..."></style>

Bu sayede kodlar okunaklı bir şekilde görüntülenebiliyor, CommonJS modüllerini5 kullanabiliyoruz, component kapsamlı (component-scpoed) CSS’ler yazabilmekteyiz6, Pug, Babel (ES2015 modülleriyle) ve Stylus gibi araçlardan faydalanabilmekteyiz.

<template lang="jade">
div
  p {{ meesage }}
  diger-component
</template>

<script>
import digerComponent from './digerComponent.vue'
export default {
  components: {
    digerComponent
  },
  data () {
    return {
      message: 'Hello World!'
    }
  }
}
</script>

<style lang="stylus" scoped>
p
  font-size 2em
  text-align center
</style>

Yukarıdaki her iki örnekte de görüldüğü üzere component instance’ları export default içerisinde ve data function olarak tanımlamaktayız. Bu arada, yukarıdaki Jade sadece kullanılabilecek modern web geliştirme araçlarından sadece biri; Bublé, TypeScript, SCSS, PostCSS, Webpack kullanıyorsanız vue-loader ve daha pek çok seçenek kolaylıkla projelerinize entegre edilebilir.

Not: Vue-loader ya da vueify kullanımında, *.vue içeriğindeki template’ler önceden derlenmiş (pre-compiled) olacaklar. Bu nedenle projeyi paketlerken (final bundle) bir compiler’a ihtiyacınız olmayacaktır. Bu nedenle sadece runtime-only opsiyonunu1 kullanabilirsiniz.

Single-file component konusuna bir eklemede bulunmak istiyorum. Yukarıdaki yapıda dosyalar oluşturmak için temel düzeyde NPM (Node Package Manager) ve Modern JavaScript (ES2015/16) bilgisine sahip olmanız gerekiyor. Peki, farklı bir yöntem söz konusu olamaz mı?

Tarayıcıda Vue.js Single-File JavaScript Bileşenleri Çalıştırmak

Vue Kurulum ve Kullanım Yöntemleri başlıklı yazının son bölümünde Vue’nun 2.6 sürümü itibariyle internet tarayıcıları için ES Module (ESM) build seçeneğini (vue.esm.browser.js) sunduğunu belirtmiştim3.

<script type="module">...</script>

Paylaşımlı bir hosting alanı kullanıyor olabilirsiniz ya da başka bir nedenden dolayı NPM kullanamıyor olabilirsiniz. Bu durumda *.vue dosyalarını yapılandırmanız pek mümkün olmayacaktır. Peki, bu durumda nasıl ilerleyebiliriz?

İnternet tarayıcıların gün geçtikçe daha donanımlı hale geldiği aşikar ve yakın zamandır native JavaScript modülleri de desteklenmeye başlandı. Bu sayede Vue component’lerini dosyalarımız içerisinde herhangi bir yapılandırma adımı gerekmeksizin organize bir şekilde kullanabilmemiz de mümkün hale geldi.

Bu işlemi 2 şekilde gerçekleştirebiliriz. İlki, Vue CLI üzerinden simple yapılandırması.

vue init simple yeni-proje

Elbette bu işlem için VUE CLI yapılandırmış olmamız gerekecek. Yani, NPM’e yine ihtiyacımız olacak. Diğer yöntemde ise tamamen el yordamı ile ilerlemekteyiz. Hemen bir örnekle başlayalım ve bir index.html dosyası oluşturalım.

<!DOCTYPE html>
<html><head>
 <title>Vue Example</title>
 <meta charset="utf-8" />
 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
</head><body>
 <section id="app" class="section" v-cloak>
  <single-file-component></single-file-component>
 </section>
 <script type="module" src="./app.mjs"></script>
</body></html>

index.html dosyamız içerisinde gördüğünüz üzere single-file-component adında bir component’imiz yer alıyor. Asıl önemli bölüm ise sonda yer alan modul tanımımız.

<script type="module" src="./app.mjs"></script>

type ifadesini farklı bir tanım (mime type) yaptığınızda Uncaught SyntaxError: Unexpected identifier hatası alırsınız. Çünkü, ilgili type tanımı content-type‘i ifade etmekte7. Diğer yandan, index.html dosyasını çift tıklatarak görüntülemek istediğinizde, özellikle Chrome kullanıyorsanız, CORS policy uyarısını almanız oldukça muhtemel.

Access to script at 'file:///.../app.mjs' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.

Dolayısıyla bu işlemler için basit bir web server’a ihtiyacınız olacak. macOS kullanıyorsanız macOS X Üzerinde Kurulumsuz PHP Çalıştırmak başlıklı yazıma göz atabilirsiniz.

.htaccess mime type - addhandler

Elbette hepsi bu kadar değil. mjs (*.mjs) dosyalarını8 çalıştırabilmek için ayrıca sunucu yapılandırmasına (Module Identifier: mime_module, Source File: mod_mime.c)9 ihtiyaç duyulmakta. Normal şartlarda .htaccess dosyasına şu tanımları eklemeniz yeterli olacaktır10.

<IfModule mime_module>
  AddHandler application/javascript .mjs
</IfModule>

<IfModule mod_mime.c>
  AddType text/javascript js mjs
</IfModule>

<IfModule mod_mime.c>
    AddCharset utf-8 .mjs
</IfModule>

Eğer hala Failed to load module script hatası alıyorsanız mime type tanımlama işlemini doğrudan mime.types dosyası üzerinden yapmamız gerekecektir.

MIME Type
Failed to load module script: The server responded with a non-JavaScript MIME type of "". Strict MIME type checking is enforced for module scripts per HTML spec.

Bunun için şu adımları izleyebilirsiniz:

  • apache2 içerisindeki mime.types dosyasını terminal üzerinden yönetici modunda açalım;
sudo nano /private/etc/apache2/mime.types
  • application/javascript satırını ve bu satıra ilişik js alanına mjs yazalım ve dosyası kayıt edip kapatalım11.
application/javascript  js mjs
  • Sunucuyu yeniden başlatalım.
sudo apachectl restart
Apache mime.type - mjs

Evet, hepsi bu kadar. Aynı işlemi NGINX için de yapmak için conf > mime.types adımlarını izlemeliyiz12. types{} içerisinde pek çok tanım yapıldığını görebilirsiniz. Ardından, applicationjavascript için yapılan js tanımını js mjs şeklinde güncellemeniz yeterli. Aynı şekilde, dosyayı kaydetip kapatmalı ve sunucunu yeniden başlatmalısınız.

types {
//...
  application/javascript  js mjs;
//...
}

Bu adımların ardından module kullanımında bir sorun yaşamıyor olmamız gerekiyor. O halde, yukarıdaki örneğimize devam edelim ve app.mjs dosyamızı oluşturalım.

import singleFileComponent from './comp.mjs';

var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue 2.6.0+ ESM Browser Build!'
  },
  components: {
    singleFileComponent
  }
});

İlk satırda bir import işlemi görmektesiniz. Module kullanımının bize sağladığı avantajlardan biri bu.

import singleFileComponent from './comp.mjs';

Bu sayede modüler bir şekilde component’lerimizi ayrı ayrı organize edebilmekteyiz. Şimdi de bu comp.mjs isimli component’imizi oluşturalım.

export default {
  template: `
  <div>
  <h1>Single-file JavaScript Component</h1>
  <p>{{ message }}</p>
  </div>
  `,
  data() {
    return {
      message: 'Oh hai from the component'
    }
  }
}

Hepsi bu kadar!

Vue Single File Component

Görüldüğü üzere farklı dosyalar içerisinde component’lerimizi kendi data function’ları ve template içerikleriyle birlikte oluşturabilmekte ve istediğimiz gibi component’ler arasında import işlemleri gerçekleştirebilmekteyiz.

http-vue-loader ve Vue Single-file Component Kullanımı

Yazının pek çok yerinde builder kullanımıyla *.vue uzantılı dosyalar arasında bir ilişki olduğundan bahsettim. Ancak, bir istisna söz konusu. İlgili dosyaları node.js ortamından bağımsız, build adımı olmaksızın çalıştırmanın bir yolu mevcut. Bu işlem için awesome-vue listesinde de yer alan http-vue-loader13 plugin’inden faydalanabiliriz. Yukarıdaki örneğimizi şimdide *.vue uzantılı dosyalarla yineleyelim. İlk olarak index.html dosyamızı oluşturalım.

<!DOCTYPE html>
<html>

<head>
 <title>Vue Example</title>
 <meta charset="utf-8" />
 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
 <script src="https://unpkg.com/vue"></script>
 <script src="https://unpkg.com/http-vue-loader"></script>
</head>

<body>

 <section id="app" class="section" v-cloak>
  <single-file-component></single-file-component>
 </section>

 <script type="text/javascript">
  var app = new Vue({
   el: '#app',
   components: {
    'single-file-component': httpVueLoader('comp.vue')
   }
  });
</script>

</body></html>

app.js dosyamıza geçelim.

var app = new Vue({
  el: '#app',
  components: {
    'single-file-component': httpVueLoader('comp.vue')
  }
});

Component tanımında, gördüğünüz üzere comp.vue dosyasını httpVueLoader() ile çağırmamız gerekmekte. comp.vue dosyamızı da oluşturalım.

<template>
  <div>
    <h1>Single-file JavaScript Component</h1>
    <p>{{ message }}</p>
  </div>
</template>

<script>module.exports = {
  data() {
    return {
      message: "Oh hai from the component"
    };
  }
};
</script>

<style>
p {
  font-size: 2em;
  text-align: center;
}
</style>

Vue tarafından Example Sandbox14 15 başlığı altında paylaşılan Simple Todo App with Vue16 17 uygulamasını module ile karşılaştırma yapabilmek için18 yeniden ele alabiliriz.

Görüldüğü üzere, vue dosyaları bize daha düzenli bir geliştirme ortamı sunmakta. İhtiyaçlarınız doğrultusunda size en uygun adımı seçebilirsiniz. Ben, sonraki örneklerde Vue CLI aracılığıyla işlemler yapacağım için ilgili adımlarda yeri geldikçe webpack’e dair de notlar paylaşacağım. Diğer yandan, module kullanımına dair yeni yazılar yayınlayacağım. Şimdilik bu kadar. Aşağıda ek anlatıların ve örneklerin yer aldığı bazı bağlantılar iletiyorum.