BelajarKoding Logobelajarkoding

Platform belajar web development Indonesia. Artikel, cheat sheets, roadmap, dan code challenges untuk developer Indonesia.

Navigasi

  • Artikel
  • Cheat Sheets
  • Roadmap
  • Challenges
  • Pricing
  • Search

Produk Lain

  • JagoHermes
  • KelasClaude
  • KilatKoding
  • BelajarVibeCoding
  • JualanKoding

Support

  • Privacy Policy
  • Terms of Service
  • Email

© 2026 BelajarKoding. All rights reserved.

Galih PratamaBagian dari ekosistem Galih Pratama
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade
belajarkoding LogobyGalih Pratama
RoadmapArtikelCheat SheetsChallengesUpgrade

Daftar Isi

Getting StartedInstallationBasic AppTemplate SyntaxText InterpolationDirectivesComposition APIsetup() Functionscript setup (Recommended)Reactivityrefreactivecomputedwatch & watchEffectLifecycle HooksComposition API LifecycleComponentsDefining ComponentsPropsEmitsv-model ComponentSlotsScoped SlotsComposablesCreating ComposablesUsing ComposablesCommon ComposablesPinia (State Management)Store SetupDefining StoresUsing StoresRouterSetupUsing RouterDirectivesBuilt-in DirectivesCustom DirectivesTeleportSuspenseTips & Best PracticesComputed vs MethodsComponent OrganizationPerformancedefineOptions (Vue 3.3+)
Vue.jsJavaScriptFrontend

Vue 3 Cheat Sheet

Referensi lengkap Vue 3 dengan Composition API. Reactive state, lifecycle, components, dan fitur modern Vue. Perfect buat frontend development.

JavaScript11 min read2.023 kata
Silakan login atau daftar untuk membaca cheat sheet ini.

#Getting Started

Panduan awal untuk memulai pengembangan aplikasi dengan Vue.js framework.

#Installation

Cara menginstall Vue.js menggunakan berbagai method seperti npm atau CDN.

bash
# Pake npm
npm create vue@latest
 
# Pake pnpm
pnpm create vue@latest
 
# Via CDN
<script src="https://unpkg.com/vue@3"></script>

#Basic App

Cara membuat aplikasi Vue.js dasar dengan setup minimal.

javascript
import { createApp } from 'vue'
import App from './App.vue'
 
createApp(App).mount('#app')

#Template Syntax

Sintaks template Vue.js untuk menampilkan data dan mengatur interaksi.

#Text Interpolation

Cara menampilkan data JavaScript dalam template menggunakan double curly braces.

vue
<template>
 <p>{{ message }}</p>
 <p>{{ count + 1 }}</p>
 <p>{{ ok ? 'YES' : 'NO' }}</p>
 <p>{{ message.split('').reverse().join('') }}</p>
</template>

#Directives

Directive Vue.js untuk mengatur behavior element HTML dengan berbagai fungsi.

vue
<template>
 <!-- v-bind - Attribute binding -->
 <img v-bind:src="imageUrl" />
 <img :src="imageUrl" /> <!-- Shorthand -->
 <div :class="{ active: isActive }"></div>
 <div :style="{ color: textColor }"></div>
 
 <!-- v-on - Event handling -->
 <button v-on:click="handleClick">Click</button>
 <button @click="handleClick">Click</button> <!-- Shorthand -->
 <input @input="handleInput" />
 
 <!-- v-model - Two-way binding -->
 <input v-model="message" />
 <textarea v-model="text"></textarea>
 <input type="checkbox" v-model="checked" />
 <select v-model="selected">
  <option>A</option>
  <option>B</option>
 </select>
 
 <!-- v-if, v-else-if, v-else -->
 <div v-if="type === 'A'">A</div>
 <div v-else-if="type === 'B'">B</div>
 <div v-else>Not A or B</div>
 
 <!-- v-show (CSS display) -->
 <div v-show="isVisible">Show me</div>
 
 <!-- v-for - List rendering -->
 <li v-for="item in items" :key="item.id">
  {{ item.name }}
 </li>
 
 <div v-for="(item, index) in items" :key="index">
  {{ index }}: {{ item }}
 </div>
 
 <div v-for="(value, key) in object" :key="key">
  {{ key }}: {{ value }}
 </div>
</template>

#Composition API

API baru Vue 3 untuk mengorganisir logic komponen dengan cara yang lebih fleksibel.

#setup() Function

Fungsi setup yang menjadi entry point untuk Composition API dalam komponen.

vue
<script>
import { ref, reactive } from 'vue'
 
export default {
 setup() {
  const count = ref(0)
  const user = reactive({
   name: 'Budi',
   age: 25
  })
 
  function increment() {
   count.value++
  }
 
  return {
   count,
   user,
   increment
  }
 }
}
</script>

#script setup (Recommended)

Sintaks script setup yang lebih sederhana untuk menggunakan Composition API.

vue
<script setup>
import { ref, reactive } from 'vue'
 
// Gak perlu return, auto-exposed ke template
const count = ref(0)
const user = reactive({
 name: 'Budi',
 age: 25
})
 
function increment() {
 count.value++
}
</script>
 
<template>
 <button @click="increment">{{ count }}</button>
 <p>{{ user.name }}</p>
</template>

#Reactivity

Sistem reaktivitas Vue.js yang secara otomatis mengupdate UI ketika data berubah.

#ref

Cara membuat reactive reference untuk primitive values.

vue
<script setup>
import { ref } from 'vue'
 
// Reactive reference
const count = ref(0)
const message = ref('Hello')
 
// Access value dengan .value
console.log(count.value) // 0
count.value++
 
// Di template gak perlu .value
</script>
 
<template>
 <p>{{ count }}</p>
</template>

#reactive

vue
<script setup>
import { reactive } from 'vue'
 
// Reactive object
const state = reactive({
 count: 0,
 message: 'Hello'
})
 
// Akses langsung, gak pake .value
state.count++
state.message = 'Hi'
 
// Nested objects juga reactive
const user = reactive({
 profile: {
  name: 'Budi',
  email: 'budi@example.com'
 }
})
 
user.profile.name = 'Ani' // Reactive
</script>

#computed

vue
<script setup>
import { ref, computed } from 'vue'
 
const firstName = ref('Budi')
const lastName = ref('Santoso')
 
// Computed property
const fullName = computed(() => {
 return firstName.value + ' ' + lastName.value
})
 
// Writable computed
const fullNameWritable = computed({
 get() {
  return firstName.value + ' ' + lastName.value
 },
 set(value) {
  [firstName.value, lastName.value] = value.split(' ')
 }
})
</script>
 
<template>
 <p>{{ fullName }}</p>
</template>

#watch & watchEffect

vue
<script setup>
import { ref, watch, watchEffect } from 'vue'
 
const count = ref(0)
const user = reactive({ name: 'Budi' })
 
// Watch specific source
watch(count, (newValue, oldValue) => {
 console.log(`Count changed from ${oldValue} to ${newValue}`)
})
 
// Watch multiple sources
watch([count, () => user.name], ([newCount, newName]) => {
 console.log(`Count: ${newCount}, Name: ${newName}`)
})
 
// Deep watch untuk object
watch(
 () => user,
 (newUser) => {
  console.log('User changed:', newUser)
 },
 { deep: true }
)
 
// watchEffect - auto track dependencies
watchEffect(() => {
 console.log(`Count is ${count.value}`)
})
 
// watchEffect dengan cleanup
watchEffect((onCleanup) => {
 const timer = setTimeout(() => {
  console.log(count.value)
 }, 1000)
 
 onCleanup(() => {
  clearTimeout(timer)
 })
})
</script>

#Lifecycle Hooks

#Composition API Lifecycle

vue
<script setup>
import {
 onBeforeMount,
 onMounted,
 onBeforeUpdate,
 onUpdated,
 onBeforeUnmount,
 onUnmounted
} from 'vue'
 
// Before component is mounted
onBeforeMount(() => {
 console.log('Before Mount')
})
 
// After component is mounted
onMounted(() => {
 console.log('Mounted')
 // Good place buat API calls, DOM manipulation
})
 
// Before component updates
onBeforeUpdate(() => {
 console.log('Before Update')
})
 
// After component updates
onUpdated(() => {
 console.log('Updated')
})
 
// Before component unmounts
onBeforeUnmount(() => {
 console.log('Before Unmount')
 // Cleanup subscriptions, timers, etc
})
 
// After component unmounts
onUnmounted(() => {
 console.log('Unmounted')
})
</script>

#Components

#Defining Components

vue
<!-- MyComponent.vue -->
<script setup>
import { ref } from 'vue'
 
const count = ref(0)
</script>
 
<template>
 <button @click="count++">{{ count }}</button>
</template>
 
<style scoped>
button {
 background: blue;
 color: white;
}
</style>

#Props

vue
<script setup>
// Define props
const props = defineProps({
 title: String,
 count: {
  type: Number,
  default: 0,
  required: true
 },
 user: Object,
 tags: Array
})
 
// Dengan TypeScript
const props = defineProps<{
 title: string
 count?: number
 user: { name: string; age: number }
}>()
 
// Access props
console.log(props.title)
console.log(props.count)
</script>
 
<template>
 <h1>{{ title }}</h1>
 <p>{{ count }}</p>
</template>

#Emits

vue
<script setup>
// Define emits
const emit = defineEmits(['update', 'delete'])
 
// Dengan validation
const emit = defineEmits({
 update: (value) => {
  return typeof value === 'string'
 },
 delete: null
})
 
// Dengan TypeScript
const emit = defineEmits<{
 update: [value: string]
 delete: [id: number]
}>()
 
// Emit event
function handleUpdate() {
 emit('update', 'new value')
}
 
function handleDelete() {
 emit('delete', 123)
}
</script>
 
<template>
 <button @click="handleUpdate">Update</button>
 <button @click="handleDelete">Delete</button>
</template>

#v-model Component

vue
<!-- Parent -->
<template>
 <CustomInput v-model="text" />
</template>
 
<!-- CustomInput.vue -->
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
 
function handleInput(e) {
 emit('update:modelValue', e.target.value)
}
</script>
 
<template>
 <input
  :value="modelValue"
  @input="handleInput"
 />
</template>

#Slots

vue
<!-- Parent -->
<template>
 <Card>
  <template #header>
   <h1>Title</h1>
  </template>
 
  <template #default>
   <p>Content here</p>
  </template>
 
  <template #footer>
   <button>OK</button>
  </template>
 </Card>
</template>
 
<!-- Card.vue -->
<template>
 <div class="card">
  <div class="card-header">
   <slot name="header"></slot>
  </div>
  <div class="card-body">
   <slot></slot> <!-- Default slot -->
  </div>
  <div class="card-footer">
   <slot name="footer"></slot>
  </div>
 </div>
</template>

#Scoped Slots

vue
<!-- Parent -->
<template>
 <UserList>
  <template #default="{ user }">
   <p>{{ user.name }} - {{ user.email }}</p>
  </template>
 </UserList>
</template>
 
<!-- UserList.vue -->
<script setup>
const users = ref([
 { id: 1, name: 'Budi', email: 'budi@example.com' },
 { id: 2, name: 'Ani', email: 'ani@example.com' }
])
</script>
 
<template>
 <div v-for="user in users" :key="user.id">
  <slot :user="user"></slot>
 </div>
</template>

#Composables

#Creating Composables

javascript
// useCounter.js
import { ref, computed } from 'vue'
 
export function useCounter(initialValue = 0) {
 const count = ref(initialValue)
 
 const doubled = computed(() => count.value * 2)
 
 function increment() {
  count.value++
 }
 
 function decrement() {
  count.value--
 }
 
 function reset() {
  count.value = initialValue
 }
 
 return {
  count,
  doubled,
  increment,
  decrement,
  reset
 }
}

#Using Composables

vue
<script setup>
import { useCounter } from './composables/useCounter'
 
const { count, doubled, increment, decrement, reset } = useCounter(10)
</script>
 
<template>
 <p>Count: {{ count }}</p>
 <p>Doubled: {{ doubled }}</p>
 <button @click="increment">+</button>
 <button @click="decrement">-</button>
 <button @click="reset">Reset</button>
</template>

#Common Composables

javascript
// useFetch.js
import { ref } from 'vue'
 
export function useFetch(url) {
 const data = ref(null)
 const error = ref(null)
 const loading = ref(false)
 
 async function fetchData() {
  loading.value = true
  try {
   const response = await fetch(url)
   data.value = await response.json()
  } catch (e) {
   error.value = e
  } finally {
   loading.value = false
  }
 }
 
 return { data, error, loading, fetchData }
}
 
// useLocalStorage.js
import { ref, watch } from 'vue'
 
export function useLocalStorage(key, defaultValue) {
 const value = ref(
  JSON.parse(localStorage.getItem(key)) ?? defaultValue
 )
 
 watch(value, (newValue) => {
  localStorage.setItem(key, JSON.stringify(newValue))
 }, { deep: true })
 
 return value
}

#Pinia (State Management)

#Store Setup

bash
npm install pinia
javascript
// main.js
import { createPinia } from 'pinia'
 
const pinia = createPinia()
app.use(pinia)

#Defining Stores

javascript
// stores/counter.js
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
 
export const useCounterStore = defineStore('counter', () => {
 // State
 const count = ref(0)
 
 // Getters
 const doubled = computed(() => count.value * 2)
 
 // Actions
 function increment() {
  count.value++
 }
 
 function decrement() {
  count.value--
 }
 
 return {
  count,
  doubled,
  increment,
  decrement
 }
})
 
// Options API style
export const useCounterStore = defineStore('counter', {
 state: () => ({
  count: 0
 }),
 getters: {
  doubled: (state) => state.count * 2
 },
 actions: {
  increment() {
   this.count++
  }
 }
})

#Using Stores

vue
<script setup>
import { useCounterStore } from '@/stores/counter'
 
const counter = useCounterStore()
 
// Access state
console.log(counter.count)
 
// Access getters
console.log(counter.doubled)
 
// Call actions
counter.increment()
 
// Destructure dengan storeToRefs
import { storeToRefs } from 'pinia'
const { count, doubled } = storeToRefs(counter)
const { increment, decrement } = counter
</script>

#Router

#Setup

bash
npm install vue-router@4
javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'
 
const routes = [
 {
  path: '/',
  name: 'home',
  component: Home
 },
 {
  path: '/about',
  name: 'about',
  component: About
 },
 {
  path: '/user/:id',
  name: 'user',
  component: () => import('@/views/User.vue') // Lazy loading
 }
]
 
const router = createRouter({
 history: createWebHistory(),
 routes
})
 
export default router

#Using Router

vue
<script setup>
import { useRouter, useRoute } from 'vue-router'
 
const router = useRouter()
const route = useRoute()
 
// Navigate
function goToAbout() {
 router.push('/about')
 router.push({ name: 'about' })
 router.push({ path: '/user/123' })
}
 
// Access params
console.log(route.params.id)
 
// Access query
console.log(route.query.search)
</script>
 
<template>
 <!-- router-link -->
 <router-link to="/">Home</router-link>
 <router-link :to="{ name: 'about' }">About</router-link>
 
 <!-- router-view -->
 <router-view />
</template>

#Directives

#Built-in Directives

vue
<template>
 <!-- v-text -->
 <span v-text="message"></span>
 
 <!-- v-html (hati-hati XSS!) -->
 <div v-html="htmlContent"></div>
 
 <!-- v-cloak -->
 <div v-cloak>{{ message }}</div>
 
 <!-- v-once - render sekali -->
 <span v-once>{{ message }}</span>
 
 <!-- v-memo - memoize subtree -->
 <div v-memo="[count]">
  {{ count }}
 </div>
</template>

#Custom Directives

vue
<script setup>
// Directive definition
const vFocus = {
 mounted(el) {
  el.focus()
 }
}
 
const vColor = {
 mounted(el, binding) {
  el.style.color = binding.value
 },
 updated(el, binding) {
  el.style.color = binding.value
 }
}
</script>
 
<template>
 <input v-focus />
 <p v-color="'red'">Red text</p>
</template>

#Teleport

vue
<template>
 <!-- Teleport ke body -->
 <Teleport to="body">
  <div class="modal">
   <p>Modal content</p>
  </div>
 </Teleport>
 
 <!-- Teleport ke selector -->
 <Teleport to="#modal-container">
  <div>Content</div>
 </Teleport>
</template>

#Suspense

vue
<template>
 <Suspense>
  <!-- Component yang async -->
  <template #default>
   <AsyncComponent />
  </template>
 
  <!-- Loading state -->
  <template #fallback>
   <div>Loading...</div>
  </template>
 </Suspense>
</template>
 
<script setup>
// AsyncComponent.vue
const data = await fetch('/api/data').then(r => r.json())
</script>

#Tips & Best Practices

#Computed vs Methods

vue
<script setup>
import { ref, computed } from 'vue'
 
const count = ref(0)
 
// Computed - cached, cuma re-evaluate kalo dependencies berubah
const doubled = computed(() => count.value * 2)
 
// Method - always execute
function getDoubled() {
 return count.value * 2
}
</script>
 
<template>
 <!-- Computed - efficient -->
 <p>{{ doubled }}</p>
 
 <!-- Method - execute setiap render -->
 <p>{{ getDoubled() }}</p>
</template>

#Component Organization

vue
<script setup>
// 1. Imports
import { ref, computed, onMounted } from 'vue'
import MyComponent from './MyComponent.vue'
 
// 2. Props & Emits
const props = defineProps(['title'])
const emit = defineEmits(['update'])
 
// 3. State
const count = ref(0)
 
// 4. Computed
const doubled = computed(() => count.value * 2)
 
// 5. Functions
function increment() {
 count.value++
}
 
// 6. Lifecycle
onMounted(() => {
 console.log('Mounted')
})
</script>

#Performance

vue
<script setup>
import { shallowRef, shallowReactive } from 'vue'
 
// Shallow reactivity - cuma top-level yang reactive
const state = shallowReactive({
 foo: 1,
 nested: {
  bar: 2
 }
})
 
// Bisa update nested tapi gak trigger reactivity
state.nested.bar = 3 // Won't trigger update
 
// Update top-level bakal trigger
state.foo = 2 // Will trigger
</script>

#defineOptions (Vue 3.3+)

vue
<script setup>
// Set component options
defineOptions({
 name: 'MyComponent',
 inheritAttrs: false
})
</script>

Baca Cheat Sheet Lengkap

Login atau daftar akun gratis untuk membaca cheat sheet ini.

LoginDaftar Gratis
Share: