VueJS ile global state kullanmadan modüler kodlama: Composables

Global state doğru kullanılmadığında projenin karmaşasına karmaşa katan tehlikeli bir teknoloji. Framework’ler de bu gerçeği fark etti ve global state kullanımı her geçen gün azalıyor. Üzerinde çalıştığınız proje, modüllere ayrılmış ve iyi organize edilmişse, global state’ten çıkmak istiyorsanız VueJS’te composables tam size göre.

Safa Gayret
3 min readDec 9, 2023

Geçen yıllarda VueJS’in resmi global state manager’ı Vuex hakkında şimdiki konuya ışık tutacak bir giriş yapmıştım. Bunu seven bunu da sever :)

Bugün sıfırdan başlanan VueJS projelerinde artık Vuex kullanılmıyor. Ama hayır :) konumuz benim ileri görüşlülüğüm değil :)

Görsel kaynağı: freepik.com

Composables nedir?

Sadece VueJS 3 ve üzeri versiyonlarda çalışır.

Doküman diyor ki, “Composables, VueJS’te mantıksal işlevselliği düzenlemek ve paylaşmak için kullanılan bir konsepttir. Bir komponent içinde kullanılabilen bağımsız ve tekrar kullanılabilir işlevsel parçalardır.”

Yani en temel haliyle bir sayfa veya komponent içerisinde ihtiyaç duyduğunuz methodu dışarıda bir yerde yazıp, projenin istediğiniz yerlerinde kullanabileceksiniz. E şimdiye kadar bunu utils.js gibi dosyalarla çözemiyor muyduk diyeceksiniz, farkı var. Composables içerisinde computed, reactive gibi VueJS’ten gelen tüm özellikleri kullanabiliyoruz. Üstelik composables içerisindeki methodları bir komponent’e import edip kullanmak bu iki kelimeyi yazmak kadar kolay. Böylece aslında composables’ı açıklarken dolaylı bir kendinden işlevli global state de diyebiliriz ama demeyelim, VueJS’ciler kızar :) Composables ile global state yaklaşımlarının performanslarını kıyaslayacak olsak composables çok büyük bir farkla yarışı kazanır.

Konu dışı ama utils.js gibi şeyler kullanmayın. utils.js, common.js, global.js… gibi içerisinde neler olduğu isminden veya projedeki konumundan anlaşılmayan dosyalar kötü kodlanmış global state’ten daha kötü bir fikir. utils.js oluşturup içerisinde birbirinden bağımsız yığınla method barındırmak yerine, birbiri ile alakalı, benzer yetkinlikleri olan methodları ayrı ayrı JS dosyalarında gruplandırın. auth.js, date-formatter.js gibi…

Gerçek dünya örneği

Bir e-ticaret uygulamasında sepet yönetimi için bir composable oluşturalım. Bu composable, alışveriş sepetindeki ürünleri eklemek, çıkarmak, toplam fiyatı hesaplamak gibi işlevleri içerebilir.

// useShoppingCart.js

import { ref, computed } from 'vue'

const useShoppingCart = () => {
// Sepet içindeki ürünleri tutan bir array
const cartItems = ref([])

// Sepete ürün eklemek için bir fonksiyon
const addToCart = (product) => {
cartItems.value.push(product)
};

// Sepetten ürün çıkarmak için bir fonksiyon
const removeFromCart = (index) => {
cartItems.value.splice(index, 1)
};

// Sepetin toplam fiyatını hesaplamak için bir computed property
const totalAmount = computed(() => {
return cartItems.value.reduce((total, item) => total + item.price, 0);
})

// Composable'da kullanılabilir fonksiyonları döndürme
return {
cartItems,
addToCart,
removeFromCart,
totalAmount,
}
}

export default useShoppingCart;

Bu composable, sepetle ilgili temel işlevleri içerir. Şimdi, bu composable’ı kullanarak bir komponent oluşturalım

<!-- ShoppingCart.vue -->

<script setup>
import useShoppingCart from './useShoppingCart'

const { cartItems, addToCart, removeFromCart, totalAmount } = useShoppingCart()
</script>

<template>
<div>
<h2>Alışveriş Sepeti</h2>
<ul>
<li v-for="(item, index) in cartItems" :key="index">
{{ item.name }} - {{ item.price }}₺
<button @click="removeFromCart(index)">Sepetten Çıkar</button>
</li>
</ul>
<p>Toplam Fiyat: {{ totalAmount }}₺</p>
</div>
</template>

Composable içerisinden gelen methodları ve değişkenleri implemente ettik ve komponent tamamlandı. İşte bu kadar kolay.

Bu kullanım, alışveriş sepeti gibi belirli işlevleri paylaşan farklı bileşenleriniz varsa çok faydalı olabilir. Composables, kodunuzu daha modüler ve okunabilir hale getirmenin yanı sıra, farklı bileşenler arasında işlevselliği paylaşmanın etkili bir yolunu sunar.

Örneğin footer’da sepetteki toplam tutarı göstermeniz gerekti.

<!-- Footer.vue -->

<script setup>
import useShoppingCart from './useShoppingCart'

const { totalAmount } = useShoppingCart()
</script>

<template>
<div>
Burası bir footer'dır. Sepetteki ürünlerin toplam tutarı {{ totalAmount }}
</div>
</template>

Mükemmel değil mi?

Dahası sepetle ilgili bir düzenleme veya geliştirme yapacağınız zaman nerede yapacağınızı biliyorsunuz. Kodun okunurluğu ve bakımı fazlasıyla kolay.

File structure önerisi

VueJS dokümanlarında composables için src altında ayrı bir klasör açılması önerildiğini görürsünüz ama bunun yerine tamamen kendi pratiklerim sonucunda aşağıdaki yapıyı öneriyorum.

-src
--components
---shopping-cart
----ShoppingCart.vue
----useShoppingCart.js

Bu composables konsepti sizce ne kadar pratik? Bir projeye dahil olsanız ve tamamının bu disiplinle yazılmış olduğunu görseniz kendinizi şanslı mı hissederdiniz yoksa “Ne gerek vardı!” diye mi düşünürsünüz?

--

--