vue-do-dont
⛔️ Don’ts & ✅ Dos in Vue 3
1. ❌ Kein import * as Foo
Don't
import * as Utils from "@/utils";
Warum vermeiden?
- Zieht das ganze Modul ins Bundle → kein Tree-Shaking
- Schlechtere Lesbarkeit
- Geringere IDE-Unterstützung
✅ Do
import { formatDate, parseInput } from "@/utils/date";
📚 Quelle: (Stack Overflow)(Stack Overflow).
2. ❌ Kein Store-Zugriff direkt in Komponenten
Don't
this.$store.dispatch("loadSomething"); // Options API Anti-Pattern
✅ Do
const { loadSomething } = useSomethingStore();
await loadSomething();
➡ Store-Zugriff nur über Composables in der App-Schicht!
3. ❌ Kein $emit inline im Template
Don't
<button @click="$emit('save')">Speichern</button>
✅ Do
function onSave() {
emit("save");
}
<button @click="onSave">Speichern</button>
📚 Quelle: (LearnVue)
4. ❌ Keine untypisierten Props/Refs
Don't
const isVisible = ref(false); // kein Typ
✅ Do
const isVisible = ref<boolean>(false);
const props = defineProps<{ title: string; isActive: boolean }>();
📚 Quelle: (Vue.js)(koderhq.com)
5. ❌ Kein any, lieber unknown
Don't
function parse(data: any) { ... }
✅ Do
function parse(data: unknown): ParsedData {
if (typeof data === 'string') { ... }
}
📚 Quelle: (Stack Overflow)(thoughtbot).
6. ❌ Kein ungetesteter Zugriff auf .value
Don't
doSomething(thing.value.someProp);
✅ Do
if (thing.value) {
doSomething(thing.value.someProp);
}
Oder mit Optional Chaining:
thing.value?.someProp;
📚 Quelle: (Stack Overflow)(Stack Overflow).
7. ❌ Kein v-if + v-for am gleichen Element
Don't
<li v-for="item in items" v-if="item.active">{{ item.name }}</li>
✅ Do
<template v-for="item in items">
<li v-if="item.active">{{ item.name }}</li>
</template>
(Vue.js).
📚 Quelle: (Rish's Repository)
8. ❌ Kein :key="index"
Don't
<li v-for="(item, index) in list" :key="index">{{ item.name }}</li>
✅ Do
<li v-for="item in list" :key="item.id">{{ item.name }}</li>
📚 Quelle: (Vue School)
9. ❌ Keine komplexe Logik im Template
Don't
<span>{{ a && b ? format(c) : defaultValue }}</span>
✅ Do
const displayValue = computed(() => (a && b ? format(c) : defaultValue));
<span>{{ displayValue }}</span>
📚 Quelle: (DEV Community)
10. ❌ Keine globale Registrierung aller Komponenten
Don't
app.component("BaseButton", BaseButton); // global für jedes Element
✅ Do
import BaseButton from "@/components/BaseButton.vue";
Nur gezielt global registrieren, wenn wirklich überall benötigt (z. B. Layout-Komponenten).
📚 Quelle: (LearnVue)
11. ❌ Keine Side-Effects in computed()
Don't
const total = computed(() => {
logTotal(); // side effect
return price * quantity;
});
✅ Do
const total = computed(() => price * quantity);
watch(total, () => logTotal());
📚 Quelle: (Binarcode)
12. ❌ Props nicht mutieren
Don't
props.title = "Neuer Titel"; // ❌
✅ Do
const titleCopy = ref(props.title);
titleCopy.value = "Neuer Titel";
📚 Quelle: (Rish's Repository, Michael Thiessen)
13. ❌ Keine filters verwenden (Vue 2 Legacy)
Don't
{{ price | currency }}
✅ Do
const formattedPrice = computed(() => formatCurrency(price));
{{ formattedPrice }}
📚 Quelle: (Vue 3 Migration Guide)
14. ❌ Kein ungesicherter Einsatz von v-html
Don't
<div v-html="userInput"></div>
✅ Do
- Nutze ein Sanitizer (z. B. DOMPurify)
- Verwende besser Slots oder gerenderte Komponenten
📚 Quelle: (ESLint Vue)
15. ❌ Keine tiefen Watcher ohne Bedarf
Don't
watch(myObject, () => { ... }, { deep: true });
✅ Do
- Nur verwenden, wenn wirklich nötig
- Alternativ: spezifische Schlüssel beobachten
📚 Quelle: (diginode.in)
16. ❌ Keine globalen Mixins in Masse
Don't
app.mixin({ created() { ... } });
✅ Do
- Nutze Composables
- Lokale Mixins für spezifische Komponenten
📚 Quelle: (Codez Up)
17. ❌ Kein Event-Bus für globale Events
Don't
EventBus.$emit("user-logged-in");
✅ Do
Pinia,provide/inject, oder zentraler EventHandler verwenden
📚 Quelle: (tkacz.pro)
18. ❌ Kein Zugriff auf $parent
Don't
this.$parent.doSomething();
✅ Do
- Verwende Props, Emits oder
provide/inject
📚 Quelle: (DEV Community)
19. ❌ Keine Inline-Templates
Don't
export default {
template: "<p>{{ message }}</p>",
};
✅ Do
- Verwende
.vue-SFCs (Single File Components)
📚 Quelle: (DEV Community)
20. ❌ Keine defineComponent ohne Typisierung
Don't
export default defineComponent({});
✅ Do
export default defineComponent({
name: "MyComponent",
props: {
title: { type: String, required: true },
},
});
Oder bei setup-only:
const props = defineProps<{ title: string }>();
➡ Immer explizite Typen für Props – für IDE, Autocomplete & Fehlervermeidung
21. ❌ Kein v-model ohne Namensgebung bei mehreren Props
Don't
<MyInput v-model="value" v-model="label" />
✅ Do
<MyInput v-model:value="value" v-model:label="label" />
➡ Immer benannte v-models bei mehreren Bindings
22. ❌ Kein direktes Einfügen von API- oder Store-Logik in Komponenten
Don't
const response = await axios.get("/users");
✅ Do
const { getUsers } = useUserApi();
const users = await getUsers();
➡ API-Zugriffe immer über Composables oder Services kapseln
23. ❌ Keine globale Nutzung von $t / $i18n ohne Fallbacks
Don't
$t("user.name");
✅ Do
const { t } = useI18n();
t("user.name", "Fallback Name");
➡ Lokale Instanz nutzen, Fallbacks immer mitdenken für Robustheit
24. ❌ Keine Logik in watchEffect, wenn nicht reaktiv notwendig
Don't
watchEffect(() => {
if (data.value) fetchDetails();
});
✅ Do
watch(data, () => {
fetchDetails();
});
➡ watchEffect nur bei komplexen reaktiven Szenarien sinnvoll – ansonsten watch()
25. ❌ Kein ref(null) bei Objekten ohne Typ
Don't
const user = ref(null);
✅ Do
const user = ref<User | null>(null);
➡ Immer konkret typisieren, besonders bei Objekten & Arrays
26. ❌ Kein direktes Schreiben in Reactive-Objekte in Templates
Don't
<input v-model="user.name" />
➡ Falsch, wenn user aus defineProps() kommt!
✅ Do
const editableUser = ref({ ...props.user });
<input v-model="editableUser.value.name" />
➡ Props dürfen nie direkt verändert werden
27. ❌ Kein wildes Verschachteln von v-if, v-for, v-show etc.
Don't
<div v-if="a">
<div v-show="b">
<ul v-for="item in items">
...
</ul>
</div>
</div>
✅ Do
Kapsle in Computed Properties oder eigene Subkomponenten
const shouldShowList = computed(() => a && b && items.length);
<template v-if="shouldShowList">
<ItemList :items="items" />
</template>
➡ Mehr Klarheit, bessere Testbarkeit, bessere DX
28. ❌ Keine vermischte Nutzung von setup() und Options-API
Don't
export default {
setup() {
...
},
data() {
return { foo: true };
}
};
✅ Do
→ Ganz oder gar nicht: entweder Composition API oder Options API
✅ Exakt – !!entgeltschluessel.model.value ist die komprimierte, idiomatische Variante
Hier ist der Do/Don't-Eintrag mit diesem Pattern eingebaut:
29. ❌ Kein überladener Vergleich auf "Wert vorhanden"
Don't
if (
entgeltschluessel.model.value !== "" &&
entgeltschluessel.model.value !== null &&
entgeltschluessel.model.value !== undefined
) {
// ...
}
✅ Do
if (!!entgeltschluessel.model.value) {
// Wert ist truthy (nicht leer, null oder undefined)
}
➡ Noch klarer (wenn explizit erwartet wird, dass .value ein String ist):
const hasValue = !!entgeltschluessel.model.value?.trim();
🧠 Hinweis:
!!foo ist idiomatisch für „hat einen gültigen Wert“
– aber: bei numerischen oder boolean-Werten kann 0 oder false fälschlich als „leer“ interpretiert werden.
Wenn das relevant ist (z. B. Zahl 0 ist gültig), dann lieber explizit prüfen:
if (entgeltschluessel.model.value !== null && entgeltschluessel.model.value !== undefined)
📚 Quelle:
„Um festzustellen, ob ein Wert in JavaScript wahr oder falsch ist, verwenden die doppelte Negation (
!!value). Damit wird der Wert sauber und sicher in einen booleschen Wert umgewandelt.“ MDN Web Docs: Boolean coercion using !!
30. ⛔️ Keine Imports innerhalb von Funktionen
❌ Anti-Pattern
function fetchData() {
const { useApi } = await import("@/composables/useApi");
return useApi().getData();
}
✅ Do
import { useApi } from "@/composables/useApi";
function fetchData() {
return useApi().getData();
}
➡ Dynamische Imports nur bei echter Lazy-Loading-Strategie in z. B. Router Guards oder Heavy Modules.
📚 Quelle: Vue Style Guide – Rule: Avoid Imports in Functions
31. 🔐 Zugriffsschutz über readonly()
Verwende readonly() für Props oder interne State-Weitergaben, um versehentliche Mutationen zu verhindern.
✅ Do
const readonlyUser = readonly(user);
➡ Readonly schützt z. B. Stores davor, außerhalb der Actions verändert zu werden.
📚 Quelle: Vue.js Guide – readonly()
32. 🧪 Hook-basierte Tests mit mountBuilder
Erstelle eigene Mount-Hilfsfunktionen für bessere Lesbarkeit & Testwiederverwendung:
function mountComponent(props: Partial<Props> = {}) {
return mount(MyComponent, {
props: { ...defaultProps, ...props },
});
}
➡ Bessere DX, DRY-Tests, weniger Setup-Code
📚 Quelle: Vitest + Vue Testing Guide
33. 📥 Formular-Komponenten sind Controlled
Alle Form-Inputs sollten v-model mit modelValue verwenden und keine lokale Mutation durchführen.
✅ Struktur
defineProps<{ modelValue: string }>();
defineEmits<{ (e: "update:modelValue", value: string): void }>();
const onInput = (value: string) => emit("update:modelValue", value);
➡ Predictable State, bessere Testbarkeit, keine versteckten Nebeneffekte
📚 Quelle: Vue 3 Docs – Component v-model
34. ⚠️ watchEffect nur bei dynamischer Abhängigkeit
❌ Don't
watchEffect(() => {
if (user.value) loadData();
});
✅ Do
watch(user, () => {
loadData();
});
➡ watchEffect() eignet sich nur bei nicht explizit benennbaren Abhängigkeiten.
📚 Quelle: Vue Docs – watch vs. watchEffect
35. ✨ Performance: Komponenten lazy laden
✅ Do (z. B. im Router)
{
path: '/settings',
component: () => import('@/views/SettingsView.vue'),
}
➡ Tree-Shaking, besseres Initial Loading, gute DX
📚 Quelle: Vue Router Docs – Lazy Loading
36. 🧼 Composables: Präfix useXyz beibehalten
Alle Composables folgen dem Namensschema useXyz, z. B.:
useAuth();
useFormValidation();
useKlinikFilter();
➡ Erhöht Lesbarkeit, IDE-Suche, Konsistenz im Projekt
📚 Quelle: VueUse & Community Guidelines
🧩 37. ❌ Kein unstrukturierter Component Output
❌ Don't
return {
foo,
bar,
a,
b,
c,
};
✅ Do
return {
foo,
bar,
nested: {
a,
b,
c,
},
};
➡ Verbesserte DX beim Destructuring im <template> oder bei Composition-Aufrufen.
📚 Quelle: Erfahrungswerte, Clean Code Patterns, DX-Optimierung
🔒 38. ❌ Kein direkter v-model auf store.state
❌ Don't
<input v-model="store.user.name" />
✅ Do
const name = ref(store.user.name);
watch(name, (newVal) => store.setName(newVal));
➡ v-model verändert sofort Store-Daten → schwer rückverfolgbar & testbar.
📚 Quelle: Pinia Best Practices – Vue School