Vue komponent a composable vzory dizajnu
Jozo· 12 min read· 2025/03/18
VueJavaScriptFrontendVývoj webuKomponentyComposables

Vue komponent a composable vzory dizajnu

Vue komponent a composable vzory dizajnu: Návod na štart

Táto príručka poskytuje praktický prehľad Vue komponent a composable vzorov dizajnu, s tipmi a príkladmi, prispôsobenými vývojárom vo vašom startupe. Využíva poznatky z rôznych zdrojov, aby vám pomohla napísať čistší, ľahšie udržiavateľný a škálovateľný Vue aplikácie.

Vzory dizajnu komponentov

1. Vzor komponentov

Extrahovanie opätovne použiteľných komponentov z existujúcich komponentov zjednodušuje kód a zvyšuje opätovnú použiteľnosť. To podporuje princíp jednej zodpovednosti, vďaka čomu je vaša kódová základňa modulárnejšia a udržiavateľnejšia.

Tip: Identifikujte a extrahovajte skryté komponenty v rámci vášho existujúceho kódu. Hľadajte opakované prvky UI alebo logiku, ktorú je možné zapúzdriť.

Príklad:

<!-- Pred: Komplexný formulár -->
<template>
  <div>
    <label for="name">Meno:</label>
    <input type="text" id="name" v-model="name">
    <label for="email">Email:</label>
    <input type="email" id="email" v-model="email">
    <button @click="submitForm">Poslať</button>
  </div>
</template>
<!-- Po: Využívanie komponentov -->
<template>
  <div>
    <InputField label="Meno" v-model="name" type="text" />
    <InputField label="Email" v-model="email" type="email" />
    <SubmitButton @click="submitForm" />
  </div>
</template>

2. Čisté komponenty

Cieľom sú komponenty, ktoré nielen pracujú, ale aj pracujú dobre, berúc do úvahy čitateľnosť kódu, udržiavateľnosť a testovateľnosť. Čisté komponenty sú ľahké na pochopenie, úpravu a ladenie.

Tip: Napíšte komponenty, ktoré sú ľahké na pochopenie a udržiavanie. Používajte jasné konvencie pomenovania, konzistentné formátovanie a jasne definované zodpovednosti.

Príklad:

<!-- Zlé: Komponent so zmiešanými opatreniami -->
<template>
  <div>
    <button @click="handleClick">{{ buttonText }}</button>
    <div v-if="showDetails">{{ details }}</div>
  </div>
</template>
<script setup>
import { ref } from 'vue';

const buttonText = ref('Zobraziť podrobnosti');
const showDetails = ref(false);
const details = ref('');

async function handleClick() {
  showDetails.value = !showDetails.value;
  if (showDetails.value) {
    details.value = await fetchData();
  }
}
</script>
<!-- Dobrá: Komponent so zameranou zodpovednosťou -->
<template>
  <div>
    <ShowDetailsButton @click="toggleDetails" :text="buttonText" />
    <DetailsDisplay v-if="showDetails" :details="details" />
  </div>
</template>
<script setup>
import { ref } from 'vue';
import ShowDetailsButton from './ShowDetailsButton.vue';
import DetailsDisplay from './DetailsDisplay.vue';

const showDetails = ref(false);
const details = ref('');
const buttonText = ref('Zobraziť podrobnosti');

async function toggleDetails() {
  showDetails.value = !showDetails.value;
  if (showDetails.value) {
    details.value = await fetchData();
  }
}
</script>

3. Viacnásobné komponenty v jednom súbore

Na malé, samostatne uzavreté komponenty zvážte, aby ste ich zips v rovnakom súbore. To môže znížiť počet súborov vo vašom projekte a zlepšiť rýchlosť vývoja, obzvlášť pre komponenty, ktoré sú pevne viazané.

Tip: Vyhnite sa vytváram zbytočných súborov na jednoduché komponenty. Použite tento prístup na komponenty, ktoré sa používajú len na jednom mieste.

Príklad:

<template>
  <div>
    <MyButton @click="handleClick">Klikni ma</MyButton>
  </div>
</template>

<script setup>
import MyButton from './MyButton.vue';

function handleClick() {
  alert('Tlačidlo bolo kliknuté!');
}
</script>
<template>
  <button @click="$emit('click')">
    <slot></slot>
  </button>
</template>
<script setup>
defineEmits(['click']);
</script>

4. Vzor kontrolovaných rekvizít

Tento vzor vám umožňuje prepísať interný stav komponenty z rodičovského prvku. To je užitočné, keď potrebujete vynútiť stav komponenty zvonka, ako je kontrola viditeľnosti modálny alebo výberu v rozbaľovacom menu.

Tip: Použite tento vzor, keď potrebujete vynútiť stav komponenty zvonka. Presuňte rekvizity komponentu na kontrolu jej interného stavu.

Príklad:

<!-- Modal.vue -->
<template>
  <div v-if="isOpen" class="modal">
    <div class="modal-content">
      <slot></slot>
      <button @click="closeModal">Zatvoriť</button>
    </div>
  </div>
</template>
<script setup>
defineProps({
  isOpen: {
    type: Boolean,
    default: false
  }
});

const emit = defineEmits(['close']);

function closeModal() {
  emit('close');
}
</script>
<!-- Komponent rodiča -->
<template>
  <div>
    <button @click="showModal = true">Otvoriť modálny</button>
    <Modal :isOpen="showModal" @close="showModal = false">
      <p>Obsah modálnych</p>
    </Modal>
  </div>
</template>
<script setup>
import { ref } from 'vue';
import Modal from './Modal.vue';

const showModal = ref(false);
</script>

5. Metadáta komponentov

Pridajte metadáta komponentom na poskytnúť ďalšie informácie ostatným komponentom. To je možné použiť na konfiguráciu komponentov, na sto ďalších informácií alebo na uľahčenie komunikácie medzi komponentami.

Tip: Používajte metadáta na konfiguráciu komponentov alebo na sto ďalších informácií. To môže byť užitočné na nástroje alebo na poskytnúť kontext ostatným komponentom.

Príklad:

<!-- Komponent A -->
<template>
  <div>Komponent A</div>
</template>
<script>
export default {
  meta: {
    componentType: 'display'
  }
}
</script>
<!-- Komponent B -->
<template>
  <div>Komponent B</div>
</template>
<script>
export default {
  meta: {
    componentType: 'formField'
  }
}
</script>

Composable vzory dizajnu

1. Vzor objektu možnosti

Na prenos parametrov do composables používajte objekt. To umožňuje flexibilitu a škálovateľnosť. Je to preferovaný spôsob na prenos mnohých možností do composable.

Tip: Tento vzor sa používa v VueUse a je vrúcne odporúčaný, keď potrebujete nakonfigurujte správanie composable.

Príklad:

// useFetch.js
import { ref, onMounted } from 'vue';

export function useFetch(url, options = {}) {
  const data = ref(null);
  const loading = ref(false);
  const error = ref(null);

  const { method = 'GET', headers = {}, body = null } = options;

  async function fetchData() {
    loading.value = true;
    try {
      const response = await fetch(url, {
        method,
        headers,
        body: body ? JSON.stringify(body) : null
      });
      if (!response.ok) {
        throw new Error(`HTTP chyba! status: ${response.status}`);
      }
      data.value = await response.json();
    } catch (e) {
      error.value = e;
    } finally {
      loading.value = false;
    }
  }

  onMounted(() => {
    fetchData();
  });

  return { data, loading, error, fetchData };
}

// V komponente:
import { useFetch } from './useFetch';
export default {
  setup() {
    const { data, loading, error } = useFetch('/api/data', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: { key: 'value' }
    });
    return { data, loading, error };
  }
}

2. Vrátené composables

Vytvárali composables priamo v súbore komponentu, aby ste sa vyhli vytváraní nových súborov. To je obzvlášť užitočné pre composables, ktoré sú veľmi špecifické pre jeden komponent a nie sú určené na opätovné použitie.

Používajte vrátené composables na malú, špecifickú logiku komponentov. To udržiava súvisiaci kód spolu a môže zjednodušiť štruktúru komponentov.

Tip: Používajte vrátené composables na malú, špecifickú logiku komponentov, ktorá nemá byť opakovane použitá. Tento prístup je skladá spolu s komponentom a zjednodušuje štruktúru komponentov.

Príklad:

Povedzme, že máte toto v svojom komponente:

<template>
  <div>{{ formattedDate }}</div>
</template>

<script setup>
import { ref, computed } from 'vue'

const useFormattedDate = () => {
  const rawDate = ref(new Date())
  const formattedDate = computed(() => {
    const options = { year: 'numeric', month: 'long', day: 'numeric' }
    return rawDate.value.toLocaleDateString(undefined, options)
  })
  return { formattedDate }
}

const { formattedDate } = useFormattedDate()
</script>

Je to v poriadku po teraz, ale ak budete potrebovať formátovanie dátumov kdekoľvek iinde, ste stratení. Tu je refaktorovaná verzia:

// src/composables/useFormattedDate.ts
import { ref, computed } from 'vue'

export function useFormattedDate() {
  const rawDate = ref(new Date())
  const formattedDate = computed(() => {
    const options = { year: 'numeric', month: 'long', day: 'numeric' }
    return rawDate.value.toLocaleDateString(undefined, options)
  })
  return { formattedDate }
}
<!-- Vo vašom komponente -->
<template>
  <div>{{ formattedDate }}</div>
</template>

<script setup>
import { useFormattedDate } from './composables/useFormattedDate'

const { formattedDate } = useFormattedDate()
</script>

Teraz môžete opätovne používať useFormattedDate v ľubovoľnom komponente. Problém vyriešený.

3. Lepšie kódovanie Composables

Extrahovať malé kúsky logiky do funkcií, ktoré môžete ľahko opakovane používať. To podporuje opätovné používanie kódu, znižuje duplikácie a robí váš kód udržiavateľnejším.

Tip: Používajte composables na organizáciu a opätovné používanie obchodnej logiky. Myslite na composables ako na opätovne použiteľné stavebné bloky pre vašu aplikáciu.

Príklad:

// useLocalStorage.ts
import { ref, watch } from 'vue';

export function useLocalStorage<T>(key: string, defaultValue: T) {
  const storedValue = localStorage.getItem(key);
  const value = ref<T>(storedValue !== null ? JSON.parse(storedValue) : defaultValue);

  watch(
    value,
    (newValue) => {
      localStorage.setItem(key, JSON.stringify(newValue));
    },
    { deep: true }
  );

  return value;
}

// Použitie v komponente:
<script setup>
import { useLocalStorage } from './useLocalStorage';

const theme = useLocalStorage('theme', 'light');
</script>

4. Začnite s rozhraním

Pred implementáciou definujte, ako sa composable bude používať. To je forma vývoja "dizajn-prvý", ktorá vám pomôže ujasniť účel composable, vstupy a výstupy pred napísaním akéhokoľvek kódu.

Tip: Definujte vstupy (rekvizity, možnosti) a výstupy (vrátené hodnoty) composable prvý. To vám pomôže zamerať sa na API composable.

Príklad:

// Pred implementáciou: useCounter.js
// Mal by akceptovať počiatočnú hodnotu
// Mal by vrátiť počet a metódy na prírasty a pokles
// Implementácia:
import { ref } from 'vue';

export function useCounter(initialValue = 0) {
  const count = ref(initialValue);

  function increment() {
    count.value++;
  }

  function decrement() {
    count.value--;
  }

  return { count, increment, decrement };
}

5. Opätovné použitie logiky s oblasťou sloty

Používajte oblasť sloty na opätovné používanie logiky medzi komponentami jedinečným spôsobom. Oblasť sloty umožňujú rodičovskému komponentu presunúť údaje a logiku do svojho dieťaťa, čo poskytuje flexibilný spôsob zdieľania funkcií.

Tip: Oblasť sloty je možné použiť na prenos údajov a logiky z rodičovského komponentu na dieťa. To umožňuje dieťaťu vykresliť svoj obsah na základe údajov a logiky poskytnutej rodiča.

Príklad:

<!-- DataFetcher.vue -->
<template>
  <div>
    <div v-if="loading">Načítavame...</div>
    <div v-else-if="error">Chyba: {{ error }}</div>
    <div v-else>
      <slot :data="data" :loading="loading" :error="error"></slot>
    </div>
  </div>
</template>
<script setup>
import { ref, onMounted } from 'vue';

const props = defineProps({
  url: {
    type: String,
    required: true
  }
});

const data = ref(null);
const loading = ref(true);
const error = ref(null);

onMounted(async () => {
  try {
    const response = await fetch(props.url);
    if (!response.ok) {
      throw new Error(`HTTP chyba! status: ${response.status}`);
    }
    data.value = await response.json();
  } catch (e) {
    error.value = e.message;
  } finally {
    loading.value = false;
  }
});
</script>
<!-- Použitie v komponente -->
<DataFetcher url="/api/items">
  <template v-slot="{ data, loading, error }">
    <div v-if="loading">Načítavame položky...</div>
    <div v-else-if="error">Chyba: {{ error }}</div>
    <div v-else>
      <ul>
        <li v-for="item in data" :key="item.id">{{ item.name }}</li>
      </ul>
    </div>
  </template>
</DataFetcher>

Všeobecný tips a najlepšie postupy

1. Ref vs. Reaktívna

Pochopte rozdiele medzi ref a reactive a vyberte si vhodný pre váš prípad použitia. ref sa používa na primárne hodnoty, zatiaľ čo reactive sa používa na objekty a polia.

Tip: Používajte ref na primárne hodnoty a reactive na objekty a polia. To pomáha systému reaktivity Vue.

Príklad:

<script>
import { ref, reactive } from 'vue';

export default {
  setup() {
    const count = ref(0); // ref na primárnu hodnotu
    const user = reactive({ name: 'John', age: 30 }); // reaktívny na objekt
    return { count, user };
  }
}
</script>

2. Efektívna správa stavu

Štruktúra stavu v aplikáciách efektívne. To je rozhodujúce na spravovanie toku údajov a zabezpečenie, aby vaša aplikácia zostala spravovateľná s rastúcou. Zvážte vhodné knižnice na správu stavu pre väčšie aplikácie.

Tip: Na správu stavu v väčšom aplikáciách zvážte Pinia alebo Vuex. Tieto knižnice poskytujú centralizovanú správu stavu a uľahčujú spravovanie zložitého stavu aplikácie.

Príklad: (Ilustratívne - skutočná implementácia závisí od zvolenej knižnice na správu stavu)

// Príklad Pinia (Konceptuálne)
import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => ({
    isLoggedIn: false,
    user: null
  }),
  actions: {
    login(userData) {
      this.isLoggedIn = true;
      this.user = userData;
    },
    logout() {
      this.isLoggedIn = false;
      this.user = null;
    }
  }
});

3. Používajte úvodné slová na sledovanie vnorených hodnôt

Sledujte vnorené hodnoty priamo pomocou úvodných slov. To umožňuje sledovanie zmien v špecifických vlastnostiach objektu, pričom sa spúšťajú aktualizácie pri zmene týchto vlastností.

Tip: Používajte úvodné slová v možnosti watch na sledovanie vnorených vlastností objektu. To je efektívnejší a špecifickejší spôsob na sledovanie zmien.

Príklad:

<script>
import { ref, watch } from 'vue';

export default {
  setup() {
    const data = ref({ user: { name: 'John' } });

    watch(() => data.value.user.name, (newName) => {
      console.log('Meno sa zmenilo:', newName);
    });

    return { data };
  }
}
</script>

4. Vzor extrakcie podmieneného textu

Rozdelite komponenty podľa podmienenej logiky. To zlepšuje čitateľnosť a udržiavateľnosť tým, že oddelí opatrenia. Robí komponenty zrozumiteľnejšími a testovateľnými.

Tip: Ak má komponent zložitú podmienenú logiku, zvážte jej rozdelenie do menších komponentov. To vytvára zameranejšie a spravovateľnejšie komponenty.

Príklad:

<!-- Pred: Zložitá podmienená logika -->
<template>
  <div>
    <div v-if="isLoading">Načítavame...</div>
    <div v-else-if="error">Chyba: {{ error }}</div>
    <div v-else>
      <UserList v-if="users.length > 0" :users="users" />
      <NoUsersMessage v-else />
    </div>
  </div>
</template>
<!-- Po: Použitie extrahu komponentov -->
<template>
  <LoadingIndicator v-if="isLoading" />
  <ErrorMessage v-if="error" :message="error" />
  <UsersDisplay v-else :users="users" />
</template>

5. 6 dôvodov na rozdelenie komponentov

Rozdeľte komponenty na menšie časti na zlepšenie organizácie kódu a opätovného použitia. To zvyšuje čitateľnosť, udržiavateľnosť a testovateľnosť vášho kódu.

Tip: Menšie komponenty sú ľahšie na pochopenie, testovanie a údržbu. Podporujú aj opätovné použitie, keďže ich môžete používať v rôznych častiach aplikácie.

Príklad:

<!-- Pred: Monolitický komponent -->
<template>
  <div>
    <Header />
    <Sidebar />
    <MainContent />
    <Footer />
  </div>
</template>
<!-- Po: Použitie extrahu komponentov -->
<template>
  <AppLayout>
    <template v-slot:header><Header /></template>
    <template v-slot:sidebar><Sidebar /></template>
    <template v-slot:main><MainContent /></template>
    <template v-slot:footer><Footer /></template>
  </AppLayout>
</template>

6. Neprepíšte komponenty CSS

Vyhnite sa priamej úprave CSS komponenty zvonka. To môže viesť k neočakávanému správaniu a sťažuje údržbu štýlov vašej aplikácie. Zapúzdrite štýly v samotnom komponente.

Tip: Používajte rekvizity alebo sloty na úpravu vzhľadu komponentu. To umožňuje kontrolované štýlovanie a zabezpečuje, že sa zabráni neočakávaným konfliktom štýlu.

Príklad:

<!-- Button.vue -->
<template>
  <button :class="['button', variant]" @click="emit('click')">
    <slot></slot>
  </button>
</template>
<script setup>
const props = defineProps({
  variant: {
    type: String,
    default: 'primary'
  }
});

const emit = defineEmits(['click']);
</script>
<style scoped>
.button {
  /* Základné štýly tlačidla */
}
.primary {
  /* Primárne štýly tlačidla */
}
.secondary {
  /* Sekundárne štýly tlačidla */
}
</style>

Turn the best models into shipped work

Teamday installs AI employees with the right model, harness, MCP servers, workspace files, review path, and recurring mission. Stop comparing tools in isolation and put them to work.