Skip to content

Vue 3 Basics

Learn the fundamentals of Vue 3, the progressive JavaScript framework for building user interfaces.

What is Vue 3?

Vue 3 is the latest major version of Vue.js, featuring:

  • Composition API: More flexible component logic
  • Better Performance: Smaller bundle size and faster rendering
  • TypeScript Support: First-class TypeScript integration
  • Multiple Root Elements: Fragments support

Installation

CDN

html
<script src="https://unpkg.com/vue@next"></script>

npm

bash
npm install vue@next
bash
npm create vue@latest my-project
cd my-project
npm install
npm run dev

Your First Vue App

html
<!DOCTYPE html>
<html>
<head>
  <title>Vue 3 App</title>
  <script src="https://unpkg.com/vue@next"></script>
</head>
<body>
  <div id="app">
    <h1>{{ message }}</h1>
    <button @click="count++">Count: {{ count }}</button>
  </div>

  <script>
    const { createApp } = Vue
    
    createApp({
      data() {
        return {
          message: 'Hello Vue 3!',
          count: 0
        }
      }
    }).mount('#app')
  </script>
</body>
</html>

Core Concepts

Reactive Data

js
import { createApp } from 'vue'

createApp({
  data() {
    return {
      message: 'Hello World',
      count: 0,
      user: {
        name: 'John',
        age: 30
      }
    }
  }
}).mount('#app')

Template Syntax

html
<!-- Text Interpolation -->
<span>Message: {{ msg }}</span>

<!-- Raw HTML -->
<span v-html="rawHtml"></span>

<!-- Attribute Binding -->
<div v-bind:id="dynamicId"></div>
<div :id="dynamicId"></div>

<!-- Event Handling -->
<button v-on:click="doSomething"></button>
<button @click="doSomething"></button>

Directives

html
<!-- Conditional Rendering -->
<p v-if="seen">Now you see me</p>
<p v-else>Now you don't</p>

<!-- List Rendering -->
<ul>
  <li v-for="item in items" :key="item.id">
    {{ item.text }}
  </li>
</ul>

<!-- Two-way Binding -->
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>

Composition API

Basic Setup

js
import { ref, reactive, computed, onMounted } from 'vue'

export default {
  setup() {
    // Reactive state
    const count = ref(0)
    const user = reactive({
      name: 'John',
      age: 30
    })
    
    // Computed property
    const doubleCount = computed(() => count.value * 2)
    
    // Methods
    const increment = () => {
      count.value++
    }
    
    // Lifecycle
    onMounted(() => {
      console.log('Component mounted')
    })
    
    return {
      count,
      user,
      doubleCount,
      increment
    }
  }
}
vue
<script setup>
import { ref, reactive, computed, onMounted } from 'vue'

const count = ref(0)
const user = reactive({
  name: 'John',
  age: 30
})

const doubleCount = computed(() => count.value * 2)

const increment = () => {
  count.value++
}

onMounted(() => {
  console.log('Component mounted')
})
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double: {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

Components

Single File Components

vue
<!-- HelloWorld.vue -->
<script setup>
import { ref } from 'vue'

const props = defineProps({
  msg: String
})

const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>
  <div class="card">
    <button type="button" @click="count++">
      count is {{ count }}
    </button>
  </div>
</template>

<style scoped>
.card {
  padding: 2em;
}
</style>

Component Communication

vue
<!-- Parent.vue -->
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'

const message = ref('Hello from parent')

const handleChildEvent = (data) => {
  console.log('Received from child:', data)
}
</script>

<template>
  <Child 
    :message="message" 
    @custom-event="handleChildEvent"
  />
</template>
vue
<!-- Child.vue -->
<script setup>
const props = defineProps({
  message: String
})

const emit = defineEmits(['custom-event'])

const sendToParent = () => {
  emit('custom-event', 'Hello from child')
}
</script>

<template>
  <div>
    <p>{{ message }}</p>
    <button @click="sendToParent">Send to Parent</button>
  </div>
</template>

Best Practices

1. Use Composition API

  • More flexible and reusable
  • Better TypeScript support
  • Easier to test

2. Prefer <script setup>

  • Less boilerplate
  • Better performance
  • More intuitive

3. Use Reactive References Wisely

js
// For primitives
const count = ref(0)

// For objects
const user = reactive({
  name: 'John',
  age: 30
})

4. Component Naming

js
// PascalCase for components
import UserProfile from './UserProfile.vue'

// kebab-case in templates
<user-profile />

Next Steps

Resources

VitePress Development Guide