Web arayüzü yanıt verebilirliğini (responsiveness) artırmak genellikle ön ucunuzun arka uçla nasıl iletişim kurduğunu optimize etmekle başlar. Vue.js tabanlı Semaphore UI’ımızda veri yüklemesini kullanıcılar için daha hızlı ve sorunsuz hale getirecek çeşitli teknikler sunduk. Bu gönderide, sürüm 2.14 içinde uyguladığımız en etkili üç değişikliği paylaşıyoruz.


API kullanımını neden optimize etmeliyiz?

API’ler genellikle dinamik web uygulamalarının omurgasını oluşturur ancak kötü yönetilen istekler darboğazlar yaratabilir. Sıralı çağrılar veya gereksiz veri çekme işlemleri nedeniyle oluşan uzun bekleme süreleri kullanıcı deneyimini düşürür. Bu sorunları doğrudan ele alalım.


1. Promise.all() ile paralel istekler

Sorun: Sıralı İstekler

Yaygın bir tuzak, API çağrılarını birbiri ardına yapmaktır. Örneğin, önce kullanıcı ayrıntılarını, ardından siparişlerini, ardından tercihlerini getirmek:

// Sıralı yaklaşım (yavaş 😞)
async function fetchData() {
  const user = await getUser();
  const inventory = await getInventory(projectID);
  const templates = await getTemplates(projectID);
  return { user, inventory, templates };
}

Burada her istek bir öncekinin bitmesini bekler. Her çağrı 200 ms sürerse, toplam süre 600 ms olur — gözle görülür bir gecikme.

Çözüm: Paralel yürütme

Vue.js, JavaScript’in eşzamansız (asynchronous) yeteneklerinden yararlanır, böylece Promise.all() kullanarak birden fazla isteği aynı anda başlatabiliriz:

// Paralel yaklaşım (hızlı ⚡)
async function fetchDataParallel() {
  const [user, inventory, templates] = await Promise.all([
    getUser(),
    getInventory(projectID),
    getTemplates(projectID),
  ]);
  return { user, inventory, templates };
}

Bağımsız istekleri gruplandırarak, üç çağrının tümü eşzamanlı olarak çözülür. Her biri 200 ms sürerse, toplam süre 200 ms’ye düşer!

Vue ile Entegrasyon

Bir Vue bileşeninde, bu deseni yaşam döngüsü kancalarında (lifecycle hooks) veya yöntemlerinde kullanın:

export default {
  props: {
    projectID: Number,
  },
  watch: {
    async projectID() {
      await this.fetchDataParallel();
    },
  },
  async created() {
    await this.fetchDataParallel();
  },
  methods: {
    async fetchDataParallel() {
      try {
        const [
          this.user, 
          this.inventory,
          this.templates
        ] = await Promise.all([
          getUser(),
          getInventory(this.projectID),
          getTemplates(this.projectID),
        ]);
      } catch (error) {
        console.error("Failed loading data:", error);
      }
    };
  }
}

İpucu: Veriler yüklenirken kullanıcıların etkileşimde kalmasını sağlamak için bunu iskelet yükleyicilerle (skeleton loaders) birleştirin.


2. Önbelleğe alınmış API yanıtlarını yeniden kullanma

Sorun: Gereksiz veri çekme

Uygulamalar genellikle aynı verileri bileşenler arasında tekrar tekrar çeker (örneğin birden fazla görünüm tarafından istenen kullanıcı profili verileri). Bu durum bant genişliğini boşa harcar ve arayüzü yavaşlatır.

Çözüm: Önbelleğe al ve yeniden kullan

API yanıtlarını saklayın ve geçersiz kılınana kadar yeniden kullanın. Vue’nun reaktivite sistemi bunu merkezi bir depo (örneğin Pinia veya Vuex) ile kolaylaştırır:

Adım 1: Önbellek deposu oluşturun

// stores/apiCache.js
import { defineStore } from 'pinia';

export const useApiCache = defineStore('apiCache', {
  state: () => ({
    project: null,
    templates: {},
  }),
  actions: {
    async fetchProject() {
      if (!this.project) {
        this.project = await getProject();
      }
      return this.project;
    },
    async fetchTemplates(id) {
      if (!this.templates[id]) {
        this.templates[id] = await fetchTemplates(id);
      }
      return this.templates[id];
    },
  },
});

Adım 2: Depoyu Bileşenlerde Kullanın

// Component.vue
import { useApiCache } from '@/stores/apiCache';

export default {
  setup() {
    const apiCache = useApiCache();

    // Önbelleğe alınmış kullanıcıyı yeniden kullanır veya bir kez çeker
    const user = apiCache.fetchUser();

    return { user };
  },
};

İpucu: Semaphore UI’da henüz Pinia kullanmıyoruz ancak yakında ona geçmeyi planlıyoruz.

Önbellek geçersiz kılma stratejileri

3. Daha yumuşak geçişler için iskelet yükleyiciler (skeleton loaders) kullanma

Sorun: Yükleme sırasında yanıt vermeyen kullanıcı arayüzü

Optimize edilmiş API çağrılarıyla bile kullanıcılar veriler yüklenirken kısa gecikmelerle karşılaşır. Bu süre zarfındaki boş ekranlar veya donmuş arayüzler, gerçek getirme süresi minimum olsa bile uygulamanızın tamamlanmamış veya yavaş hissettirmesine neden olabilir.

Çözüm: İskelet yükleyiciler

İskelet yer tutucuları, veriler yüklenirken içeriğinizin düzenini taklit ederek kullanıcıları etkileşimde tutan görsel geri bildirim sağlar. Paralel istekler ve önbellekleme ile birleştirildiğinde kusursuz bir hız algısı yaratırlar.


Vue.js’de iskelet yükleyiciler nasıl eklenir?

Adım 1: İskelet bileşenini seçin

Semaphore UI’da Vuetify çerçevesini kullanıyoruz. Zaten iskelet yükleyici bileşenini içerir.

<v-skeleton-loader
  type="
    table-heading,
    image,
    list-item-two-line,
    list-item-two-line,
    list-item-two-line"
></v-skeleton-loader>

Adım 2: Eşzamansız veri getirme ile entegre edin İskeletler ile gerçek içerik arasında geçiş yapmak için bir yükleme durumu kullanın:

// Component.vue
export default {
  data() {
    return {
      isLoading: true,
      user: null,
      templates: [],
    };
  },
  async created() {
    this.isLoading = true;
    try {
      [this.user, this.templates] = await Promise.all([
        fetchUser(),
        fetchTemplates(),
      ]);
    } finally {
      this.isLoading = false;
    }
  },
};

Adım 3: İskeletleri koşullu olarak oluşturun

<template>
  <div class="user-profile">
    <v-skeleton-loader
      v-if="isLoading"
      type="
        table-heading,
        image,
        list-item-two-line,
        list-item-two-line,
        list-item-two-line"
    ></v-skeleton-loader>
    <v-card v-else>
      <h2>{{ user.name }}</h2>

      <v-card v-for="tpl in templates" :key="tpl.id">
        <v-card-title>{{ tpl.name }}</v-card-title>
        <v-card-text>{{ tpl.description }}</v-card-text>
      </v-card>
    </div>
  </div>
</template>

Yükleyici tasarımı için gelişmiş ipuçları

  1. İçerik yapısını eşleştirin: İskeletleri gerçek düzeninizi yansıtacak şekilde tasarlayın (örneğin görsel yer tutucuları, metin satırları).
  2. Ekranın üst kısmındaki (above-the-fold) içeriğe öncelik verin: Arka plandaki veriler tamamlanırken önce görünen içerik için yükleyicileri gösterin.
  3. Önbellekleme ile birleştirin: Önbelleğe alınmış veriler mevcutsa iskeletleri atlayın ve verileri hemen gösterin.

Bu neden önemlidir?


Hepsini bir araya getirme

Paralel istekleri, akıllı önbelleğe almayı ve iskelet yükleyicileri birleştirerek önemli iyileştirmeler elde ettik:

  1. Daha hızlı ilk yüklemeler: Paralel istekler veri yükleme sürelerini önemli ölçüde kısalttı.
  2. Daha yumuşak gezinme: Yeniden kullanılan yanıtlar gereksiz ağ çağrılarını azalttı.
  3. Gelişmiş kullanıcı etkileşimi: İskelet yükleyiciler görsel geri bildirimi ve algılanan hızı artırdı.

API kullanımını optimize etmek yalnızca ham performansla ilgili değildir; aynı zamanda kullanıcı deneyimini de geliştirir. Semaphore UI’ı her güncellemede daha hızlı ve daha kullanıcı dostu hale getirmek için ön ucumuzu iyileştirmeye devam edeceğiz.