| 1 |
Composition |
Use Composition API for new projects |
Composition API offers better TypeScript support and logic reuse |
<script setup> for components |
Options API for new projects |
<script setup> |
export default { data() } |
Medium |
Composition API FAQ |
| 2 |
Composition |
Use script setup syntax |
Cleaner syntax with automatic exports |
<script setup> with defineProps |
setup() function manually |
<script setup> |
<script> setup() { return {} } |
Low |
SFC Script Setup |
| 3 |
Reactivity |
Use ref for primitives |
ref() for primitive values that need reactivity |
ref() for strings numbers booleans |
reactive() for primitives |
const count = ref(0) |
const count = reactive(0) |
Medium |
Reactivity Fundamentals |
| 4 |
Reactivity |
Use reactive for objects |
reactive() for complex objects and arrays |
reactive() for objects with multiple properties |
ref() for complex objects |
const state = reactive({ user: null }) |
const state = ref({ user: null }) |
Medium |
- |
| 5 |
Reactivity |
Access ref values with .value |
Remember .value in script unwrap in template |
Use .value in script |
Forget .value in script |
count.value++ |
count++ (in script) |
High |
- |
| 6 |
Reactivity |
Use computed for derived state |
Computed properties cache and update automatically |
computed() for derived values |
Methods for derived values |
const doubled = computed(() => count.value * 2) |
const doubled = () => count.value * 2 |
Medium |
Computed Properties |
| 7 |
Reactivity |
Use shallowRef for large objects |
Avoid deep reactivity for performance |
shallowRef for large data structures |
ref for large nested objects |
const bigData = shallowRef(largeObject) |
const bigData = ref(largeObject) |
Medium |
shallowRef |
| 8 |
Watchers |
Use watchEffect for simple cases |
Auto-tracks dependencies |
watchEffect for simple reactive effects |
watch with explicit deps when not needed |
watchEffect(() => console.log(count.value)) |
watch(count, (val) => console.log(val)) |
Low |
Watchers |
| 9 |
Watchers |
Use watch for specific sources |
Explicit control over what to watch |
watch with specific refs |
watchEffect for complex conditional logic |
watch(userId, fetchUser) |
watchEffect with conditionals |
Medium |
- |
| 10 |
Watchers |
Clean up side effects |
Return cleanup function in watchers |
Return cleanup in watchEffect |
Leave subscriptions open |
watchEffect((onCleanup) => { onCleanup(unsub) }) |
watchEffect without cleanup |
High |
- |
| 11 |
Props |
Define props with defineProps |
Type-safe prop definitions |
defineProps with TypeScript |
Props without types |
defineProps<{ msg: string }>() |
defineProps(['msg']) |
Medium |
Typing Component Props |
| 12 |
Props |
Use withDefaults for default values |
Provide defaults for optional props |
withDefaults with defineProps |
Defaults in destructuring |
withDefaults(defineProps<Props>(), { count: 0 }) |
const { count = 0 } = defineProps() |
Medium |
- |
| 13 |
Props |
Avoid mutating props |
Props should be read-only |
Emit events to parent for changes |
Direct prop mutation |
emit('update:modelValue', newVal) |
props.modelValue = newVal |
High |
- |
| 14 |
Emits |
Define emits with defineEmits |
Type-safe event emissions |
defineEmits with types |
Emit without definition |
defineEmits<{ change: [id: number] }>() |
emit('change', id) without define |
Medium |
Typing Component Emits |
| 15 |
Emits |
Use v-model for two-way binding |
Simplified parent-child data flow |
v-model with modelValue prop |
:value + @input manually |
<Child v-model="value"/> |
<Child :value="value" @input="value = $event"/> |
Low |
v-model |
| 16 |
Lifecycle |
Use onMounted for DOM access |
DOM is ready in onMounted |
onMounted for DOM operations |
Access DOM in setup directly |
onMounted(() => el.value.focus()) |
el.value.focus() in setup |
High |
Lifecycle Hooks |
| 17 |
Lifecycle |
Clean up in onUnmounted |
Remove listeners and subscriptions |
onUnmounted for cleanup |
Leave listeners attached |
onUnmounted(() => window.removeEventListener()) |
No cleanup on unmount |
High |
- |
| 18 |
Lifecycle |
Avoid onBeforeMount for data |
Use onMounted or setup for data fetching |
Fetch in onMounted or setup |
Fetch in onBeforeMount |
onMounted(async () => await fetchData()) |
onBeforeMount(async () => await fetchData()) |
Low |
- |
| 19 |
Components |
Use single-file components |
Keep template script style together |
.vue files for components |
Separate template/script files |
Component.vue with all parts |
Component.js + Component.html |
Low |
- |
| 20 |
Components |
Use PascalCase for components |
Consistent component naming |
PascalCase in imports and templates |
kebab-case in script |
<MyComponent/> |
<my-component/> |
Low |
Component Naming |
| 21 |
Components |
Prefer composition over mixins |
Composables replace mixins |
Composables for shared logic |
Mixins for code reuse |
const { data } = useApi() |
mixins: [apiMixin] |
Medium |
- |
| 22 |
Composables |
Name composables with use prefix |
Convention for composable functions |
useFetch useAuth useForm |
getData or fetchApi |
export function useFetch() |
export function fetchData() |
Medium |
Composables |
| 23 |
Composables |
Return refs from composables |
Maintain reactivity when destructuring |
Return ref values |
Return reactive objects that lose reactivity |
return { data: ref(null) } |
return reactive({ data: null }) |
Medium |
- |
| 24 |
Composables |
Accept ref or value params |
Use toValue for flexible inputs |
toValue() or unref() for params |
Only accept ref or only value |
const val = toValue(maybeRef) |
const val = maybeRef.value |
Low |
toValue |
| 25 |
Templates |
Use v-bind shorthand |
Cleaner template syntax |
:prop instead of v-bind:prop |
Full v-bind syntax |
<div :class="cls"> |
<div v-bind:class="cls"> |
Low |
- |
| 26 |
Templates |
Use v-on shorthand |
Cleaner event binding |
@event instead of v-on:event |
Full v-on syntax |
<button @click="handler"> |
<button v-on:click="handler"> |
Low |
- |
| 27 |
Templates |
Avoid v-if with v-for |
v-if has higher priority causes issues |
Wrap in template or computed filter |
v-if on same element as v-for |
<template v-for><div v-if> |
<div v-for v-if> |
High |
Avoid v-if with v-for |
| 28 |
Templates |
Use key with v-for |
Proper list rendering and updates |
Unique key for each item |
Index as key for dynamic lists |
v-for="item in items" :key="item.id" |
v-for="(item, i) in items" :key="i" |
High |
- |
| 29 |
State |
Use Pinia for global state |
Official state management for Vue 3 |
Pinia stores for shared state |
Vuex for new projects |
const store = useCounterStore() |
Vuex with mutations |
Medium |
Pinia |
| 30 |
State |
Define stores with defineStore |
Composition API style stores |
Setup stores with defineStore |
Options stores for complex state |
defineStore('counter', () => {}) |
defineStore('counter', { state }) |
Low |
- |
| 31 |
State |
Use storeToRefs for destructuring |
Maintain reactivity when destructuring |
storeToRefs(store) |
Direct destructuring |
const { count } = storeToRefs(store) |
const { count } = store |
High |
Destructuring from a Store |
| 32 |
Routing |
Use useRouter and useRoute |
Composition API router access |
useRouter() useRoute() in setup |
this.$router this.$route |
const router = useRouter() |
this.$router.push() |
Medium |
Composition API Router |
| 33 |
Routing |
Lazy load route components |
Code splitting for routes |
() => import() for components |
Static imports for all routes |
component: () => import('./Page.vue') |
component: Page |
Medium |
Lazy Loading |
| 34 |
Routing |
Use navigation guards |
Protect routes and handle redirects |
beforeEach for auth checks |
Check auth in each component |
router.beforeEach((to) => {}) |
Check auth in onMounted |
Medium |
- |
| 35 |
Performance |
Use v-once for static content |
Skip re-renders for static elements |
v-once on never-changing content |
v-once on dynamic content |
<div v-once>{{ staticText }}</div> |
<div v-once>{{ dynamicText }}</div> |
Low |
v-once |
| 36 |
Performance |
Use v-memo for expensive lists |
Memoize list items |
v-memo with dependency array |
Re-render entire list always |
<div v-for v-memo="[item.id]"> |
<div v-for> without memo |
Medium |
v-memo |
| 37 |
Performance |
Use shallowReactive for flat objects |
Avoid deep reactivity overhead |
shallowReactive for flat state |
reactive for simple objects |
shallowReactive({ count: 0 }) |
reactive({ count: 0 }) |
Low |
- |
| 38 |
Performance |
Use defineAsyncComponent |
Lazy load heavy components |
defineAsyncComponent for modals dialogs |
Import all components eagerly |
defineAsyncComponent(() => import()) |
import HeavyComponent from |
Medium |
defineAsyncComponent |
| 39 |
TypeScript |
Use generic components |
Type-safe reusable components |
Generic with defineComponent |
Any types in components |
<script setup lang="ts" generic="T"> |
<script setup> without types |
Medium |
TypeScript Composition API |
| 40 |
TypeScript |
Type template refs |
Proper typing for DOM refs |
ref(null) |
ref(null) without type |
const input = ref<HTMLInputElement>(null) |
const input = ref(null) |
Medium |
- |
| 41 |
TypeScript |
Use PropType for complex props |
Type complex prop types |
PropType for object props |
Object without type |
type: Object as PropType<User> |
type: Object |
Medium |
- |
| 42 |
Testing |
Use Vue Test Utils |
Official testing library |
mount shallowMount for components |
Manual DOM testing |
import { mount } from '@vue/test-utils' |
document.createElement |
Medium |
Vue Test Utils |
| 43 |
Testing |
Test component behavior |
Focus on inputs and outputs |
Test props emit and rendered output |
Test internal implementation |
expect(wrapper.text()).toContain() |
expect(wrapper.vm.internalState) |
Medium |
- |
| 44 |
Forms |
Use v-model modifiers |
Built-in input handling |
.lazy .number .trim modifiers |
Manual input parsing |
<input v-model.number="age"> |
<input v-model="age"> then parse |
Low |
v-model Modifiers |
| 45 |
Forms |
Use VeeValidate or FormKit |
Form validation libraries |
VeeValidate for complex forms |
Manual validation logic |
useField useForm from vee-validate |
Custom validation in each input |
Medium |
- |
| 46 |
Accessibility |
Use semantic elements |
Proper HTML elements in templates |
button nav main for purpose |
div for everything |
<button @click> |
<div @click> |
High |
- |
| 47 |
Accessibility |
Bind aria attributes dynamically |
Keep ARIA in sync with state |
":aria-expanded="isOpen"" |
Static ARIA values |
":aria-expanded="menuOpen"" |
aria-expanded="true"" |
Medium |
- |
| 48 |
SSR |
Use Nuxt for SSR |
Full-featured SSR framework |
Nuxt 3 for SSR apps |
Manual SSR setup |
npx nuxi init my-app |
Custom SSR configuration |
Medium |
Nuxt |
| 49 |
SSR |
Handle hydration mismatches |
Client/server content must match |
ClientOnly for browser-only content |
Different content server/client |
<ClientOnly><BrowserWidget/></ClientOnly> |
<div>{{ Date.now() }}</div> |
High |
- |