Tags: vue, composition-api, lifecycle, directives, slots, pinia, frontend Last updated: 2026-06-26

Vue.js Cheatsheet

Quick Reference

ConceptComposition APIOptions API
Reactive state ref() / reactive() data()
Computed computed() computed: {}
Watchers watch() / watchEffect() watch: {}
Lifecycle onMounted(), etc. mounted(), etc.
Methods plain functions methods: {}
Props defineProps() props: {}
Emits defineEmits() emits: {}
Store useXxxStore() (Pinia) this.$store (Vuex)

Composition API (<script setup>)

Reactive State

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

const count = ref(0);
const user = reactive({ name: "", age: 0 });

count.value++;              // .value in JS
user.name = "Max";          // No .value for reactive
</script>

<template>
  <p>{{ count }}</p>      <!-- auto-unwrapped -->
</template>

computed

import { computed, ref } from "vue";
const items = ref([1, 2, 3]);
const doubled = computed(() =>
  items.value.map(i => i * 2));

watch / watchEffect

import { watch, watchEffect, ref } from "vue";
const query = ref("");

watch(query, (newVal, oldVal) => {
  fetchResults(newVal);
}, { immediate: true });

watchEffect(() => {
  console.log(query.value); // Auto-tracks deps
});

defineProps & defineEmits

const props = defineProps({
  title: String,
  count: { type: Number, default: 0 },
});
const emit = defineEmits(["update", "delete"]);
emit("update", { id: 1 });

defineModel (Vue 3.4+)

// Two-way binding shortcut
const model = defineModel();
// or with options
const model = defineModel({ type: String, default: "" });
// Template: <input v-model="model" />

Template Directives

DirectivePurposeExample
v-bind / : Bind attribute :href="url"
v-on / @ Attach event @click="handler"
v-model Two-way binding <input v-model="name">
v-if / v-else Conditional <div v-if="show">
v-show Toggle display <div v-show="ok">
v-for Loop <li v-for="i in items" :key="i.id">
v-once Render once <span v-once>{{ init }}</span>
v-memo Memoise subtree <div v-memo="[a, b]">

v-model Modifiers

<input v-model.lazy="msg">    <!-- change, not input -->
<input v-model.number="age">  <!-- cast to number -->
<input v-model.trim="name">   <!-- trim whitespace -->

Slots

Default Slot

<!-- Child -->
<div class="card"><slot>Default</slot></div>
<!-- Parent -->
<Card>Hello</Card>

Named Slots

<!-- Child -->
<header><slot name="header" /></header>
<main><slot /></main>
<footer><slot name="footer" /></footer>

<!-- Parent -->
<Layout>
  <template #header><h1>Title</h1></template>
  <p>Body</p>
  <template #footer><small>Footer</small></template>
</Layout>

Scoped Slots

<!-- Child: <slot :item="item" :index="index" /> -->
<!-- Parent -->
<template #default="{ item, index }">
  {{ index }}: {{ item.name }}
</template>

Lifecycle Hooks

CompositionOptionsWhen
onBeforeMountbeforeMount Before DOM insertion
onMountedmounted After DOM insertion
onBeforeUpdatebeforeUpdate Before re-render
onUpdatedupdated After re-render
onBeforeUnmount beforeUnmount Before removal
onUnmountedunmounted After removal
onErrorCaptured errorCaptured Child error caught

Pinia (State Management)

Define a Store

// stores/counter.ts
import { defineStore } from "pinia";

export const useCounterStore = defineStore("counter", () => {
  const count = ref(0);
  const double = computed(() => count.value * 2);
  function increment() { count.value++; }
  return { count, double, increment };
});

// Options store style
export const useCounterStore = defineStore("counter", {
  state: () => ({ count: 0 }),
  getters: { double: (s) => s.count * 2 },
  actions: { increment() { this.count++; } },
});

Using a Store

import { useCounterStore } from "@/stores/counter";
import { storeToRefs } from "pinia";

const store = useCounterStore();
// Reactive destructure of state + getters
const { count, double } = storeToRefs(store);
// Methods destructure directly
const { increment } = store;

Component Communication

Props Down, Events Up

<!-- Parent -->
<Child :title="title" @update="title = $event" />

<!-- Child -->
const props = defineProps({ title: String });
const emit = defineEmits(["update"]);
emit("update", "new title");

provide / inject

// Ancestor
provide("theme", ref("dark"));
// Descendant
const theme = inject("theme", "light"); // default fallback

Teleport

<Teleport to="body">
  <Modal />
</Teleport>
<Teleport to="#target" :disabled="isMobile">
  <Tooltip />
</Teleport>

TypeScript with Vue

<script setup lang="ts">
const user = ref<User>({ name: "", age: 0 });
const props = defineProps<{
  title: string; count?: number
}>();
const emit = defineEmits<{
  update: [id: number]; close: []
}>();
</script>

Tips