Introduction to Vue.js

  1. What Is Vue.js?



  2. How Vue Works

  3. <div id="app">
        {{ message }}
    </div>
    
    <script>
    const app = Vue.createApp({
        data() {
            return {
                message: "Hello Vue!"
            }
        }
    });
    app.mount("#app");
    </script>
    


  4. Getting Started (CDN Method)

  5. <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    


  6. Core Concepts of Vue

    1. Templates & Data Binding

    2. <div id="app">
          <h1>{{ title }}</h1>
          <p>Counter: {{ count }}</p>
      </div>
      
      data() {
          return {
              title: "Welcome",
              count: 0
          }
      }
      


    3. Event Handling

    4. <button @click="count++">Increase</button>
      


    5. Two-Way Binding with v-model

    6. <input v-model="name">
      <p>Hello {{ name }}</p>
      


    7. Conditionals and Loops
    8. <p v-if="loggedIn">Welcome back!</p>
      <p v-else>Please log in.</p>
      
      <ul>
          <li v-for="item in items">{{ item }}</li>
      </ul>
      


    9. Computed Properties

    10. computed: {
          reversed() {
              return this.message.split("").reverse().join("");
          }
      }
      


    11. Methods
    12. methods: {
          greet() {
              return "Hello " + this.name;
          }
      }
      


  7. Single File Components (SFC)

  8. <!-- MyComponent.vue -->
    <template>
        <h1>{{ title }}</h1>
    </template>
    
    <script>
    export default {
        data() {
            return { title: "Hello SFC" }
        }
    }
    </script>
    
    <style>
    h1 { color: blue; }
    </style>
    


  9. Vue Tooling: Vite & Vue CLI

  10. npm create vue@latest
    cd myproject
    npm install
    npm run dev
    


  11. Ecosystem Overview

  12. Tool Purpose
    Vue Router Navigation for SPA apps
    Pinia State management
    Vite Development & build tool
    DevTools Chrome/Firefox debugging extension


  13. Why Vue Is Great for Beginners



  14. Summary

  15. Concept Description
    Reactivity UI updates automatically when data changes
    Declarative templates Use {{ }} to display data easily
    Directives v-if, v-for, v-model, v-on
    Components Reusable UI blocks, .vue files
    Tooling Vite, Vue Router, Pinia



Vue Options API vs Composition API (Introduction & Comparison)

  1. Introduction



  2. Options API (Traditional Vue Style)

  3. export default {
        data() {
            return { count: 0 }
        },
        methods: {
            increment() {
                this.count++
            }
        },
        computed: {
            double() {
                return this.count * 2
            }
        }
    }
    


  4. Composition API (Modern Vue Style)

  5. import { ref, computed } from "vue"
    
    export default {
        setup() {
            const count = ref(0)
            const increment = () => count.value++
            const double = computed(() => count.value * 2)
    
            return { count, increment, double }
        }
    }
    


  6. Key Differences

  7. Aspect Options API Composition API
    Code organization Grouped by option (data, methods, computed) Grouped by logic and feature
    Learning curve Easier for beginners Requires understanding of JS functions and reactivity
    Reactivity Implicit (this.count++) Explicit (ref, reactive, value)
    Logic reuse Mixins (old, limited) Composable functions (modern, powerful)
    Scaling to large apps More difficult Much easier
    TypeScript support Limited Excellent
    IDE autocompletion Less precise (due to this) Very precise (explicit types & refs)
    Recommended for Vue 3? Still supported & fine Yes, the future direction


  8. When to Use Which?



  9. Example Comparison (Same Component in Both APIs)

  10. Options API Composition API
    export default {
        data() {
            return { count: 0 }
        },
        methods: {
            increment() {
                this.count++
            }
        }
    }
    
    import { ref } from "vue"
    
    export default {
        setup() {
            const count = ref(0)
            const increment = () => count.value++
            return { count, increment }
        }
    }
    


  11. Summary

  12. Feature Options API Composition API
    Ease of use Very easy Medium
    Flexibility Limited Very high
    Logic organization Separated by option Grouped by feature
    Preferred for new Vue 3 apps? Okay Yes
    TypeScript Not ideal Excellent



Introduction to the Vue 3 Composition API (Thorough & Beginner-Friendly)

  1. Short Intro



  2. The Setup Function

  3. export default {
        setup() {
            return {
                // expose things to the template
            }
        }
    }
    


  4. Creating Reactive State with ref()

  5. import { ref } from "vue"
    
    export default {
        setup() {
            const count = ref(0)
    
            function increment() {
                count.value++
            }
    
            return { count, increment }
        }
    }
    


  6. Reactive Objects with reactive()

  7. import { reactive } from "vue"
    
    export default {
        setup() {
            const state = reactive({
                name: "Hwangfu",
                age: 22
            })
    
            return { state }
        }
    }
    


  8. Computed Properties

  9. import { ref, computed } from "vue"
    
    export default {
        setup() {
            const count = ref(5)
            const double = computed(() => count.value * 2)
    
            return { count, double }
        }
    }
    


  10. Watchers

  11. import { ref, watch } from "vue"
    
    export default {
        setup() {
            const name = ref("")
    
            watch(name, (newVal, oldVal) => {
                console.log("Changed from", oldVal, "to", newVal)
            })
    
            return { name }
        }
    }
    


  12. watchEffect() (Automatic Tracking)

  13. import { ref, watchEffect } from "vue"
    
    export default {
        setup() {
            const x = ref(1)
    
            watchEffect(() => {
                console.log("x is", x.value)
            })
    
            return { x }
        }
    }
    


  14. Methods in Composition API

  15. setup() {
        const count = ref(0)
    
        function increment() {
            count.value++
        }
    
        return { count, increment }
    }
    


  16. Lifecycles in Composition API

  17. import { onMounted, onUnmounted } from "vue"
    
    export default {
        setup() {
            onMounted(() => {
                console.log("Component mounted")
            })
    
            onUnmounted(() => {
                console.log("Component removed")
            })
        }
    }
    


  18. Props in Composition API

  19. export default {
        props: {
            msg: String;
        },
    
        setup(props) {
            console.log(props.msg);
            return { };
        }
    }
    


  20. Emitting Events

  21. export default {
        emits: ["update"],
    
        setup(props, { emit }) {
            function updateValue() {
                emit("update", 42)
            }
    
            return { updateValue }
        }
    }
    


  22. Extracting Logic Into Composables

  23. // useCounter.js
    import { ref } from "vue"
    
    export function useCounter() {
        const count = ref(0)
        const increment = () => count.value++
        return { count, increment }
    }
    
    // Component.vue
    import { useCounter } from "./useCounter"
    
    export default {
        setup() {
            const { count, increment } = useCounter()
            return { count, increment }
        }
    }
    


  24. Why the Composition API Is Powerful



  25. Options API vs Composition API Summary

  26. Feature Options API Composition API
    Organization By option type (data, methods, etc.) By logic/functionality
    Reactivity Implicit this Explicit ref, reactive
    Logic reuse mixins composables
    TypeScript not ideal excellent
    Best for small/simple components medium/large apps, reusable logic



What Is a Reactive Value in Vue?

  1. Introduction



  2. Why Reactivity Matters

  3. let count = 0
    document.getElementById("counter").innerText = count
    count++
    document.getElementById("counter").innerText = count
    
    const count = ref(0)
    count.value++  // UI updates automatically
    


  4. ref(): Reactive Primitive Values

  5. import { ref } from "vue"
    
    const count = ref(0)
    console.log(count.value)  // 0
    
    count.value++
    console.log(count.value)  // 1
    


  6. reactive(): Reactive Objects (Deep Reactivity)

  7. import { reactive } from "vue"
    
    const user = reactive({
        name: "Hwangfu",
        age: 22
    })
    
    user.age++  // automatically updates UI
    



  8. How Vue Detects Changes (The Proxy System)



  9. Reactive Values in Templates

  10. <template>
        <button @click="count++">{{ count }}</button>
    </template>
    



  11. Computed Properties Are Also Reactive

  12. import { ref, computed } from "vue"
    
    const count = ref(5)
    const double = computed(() => count.value * 2)
    
    console.log(double.value) // 10
    count.value++
    console.log(double.value) // 12
    


  13. Watchers React to Changes

  14. watch(count, (newVal, oldVal) => {
        console.log("Count changed:", oldVal, "->", newVal)
    })
    


  15. How Reactivity Differs from Plain JavaScript

  16. Plain JavaScript Vue Reactive Value
    Variables don't update UI automatically UI updates instantly when reactive value changes
    No dependency tracking Automatic dependency tracking
    Manual DOM updates needed DOM updates handled by Vue


  17. ref vs reactive (Quick Comparison)

  18. Feature ref() reactive()
    Main use-case single primitive objects, arrays, maps
    Access value property direct property access
    Deep reactivity No (wrapping only) Yes
    Template auto-unwrapping Yes Yes


  19. When Should You Use ref()?

  20. const isVisible = ref(true)
    const count = ref(0)
    const message = ref("Hello")
    


  21. When Should You Use reactive()?

  22. const form = reactive({
        email: "",
        password: "",
        remember: false
    })
    


Creating a Vue Application

  1. Introduction



  2. Quick Start with CDN (Single HTML File)

  3. <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>Vue 3 App</title>
        <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    </head>
    <body>
        <div id="app">
            <h1>{{ title }}</h1>
            <button @click="count++">Clicked {{ count }} times</button>
        </div>
    
        <script>
            const { createApp, ref } = Vue;
    
            const App = {
                setup() {
                    const title = ref("Hello Vue 3");
                    const count = ref(0);
    
                    return { title, count };
                }
            };
    
            createApp(App).mount("#app");
        </script>
    </body>
    </html>
    


  4. Real Project Setup with Vite (Recommended)

  5. # 1. Create a new Vue 3 + Vite project
    npm create vue@latest my-vue-app
    
    # 2. Move into the folder
    cd my-vue-app
    
    # 3. Install dependencies
    npm install
    
    # 4. Start dev server
    npm run dev
    


  6. Project Structure (Typical Vite + Vue App)

  7. my-vue-app/
      ├─ index.html
      ├─ package.json
      ├─ vite.config.js
      └─ src/
          ├─ main.js
          ├─ App.vue
          └─ components/
              └─ ...
    


  8. main.js: Creating and Mounting the Application

  9. import { createApp } from "vue"
    import App from "./App.vue"
    
    const app = createApp(App)
    
    // here you can also use plugins, router, store, etc.
    // e.g. app.use(router)
    
    app.mount("#app")
    


  10. index.html: The Mount Point
  11. <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <title>My Vue App</title>
        </head>
        <body>
            <div id="app"></div>
            <!-- Vite will inject the compiled JS here -->
            <script type="module" src="/src/main.js"></script>
        </body>
    </html>
    


  12. App.vue: Root Component with Composition API

  13. <template>
        <main>
            <h1>{{ title }}</h1>
            <button @click="count++">
                Clicked {{ count }} times
            </button>
        </main>
    </template>
    
    <script setup>
    import { ref } from "vue"
    
    const title = ref("My First Vue App")
    const count = ref(0)
    </script>
    
    <style>
    main {
        font-family: system-ui, sans-serif;
    }
    button {
        margin-top: 1rem;
    }
    </style>
    


  14. Adding Another Component

  15. <template>
        <div>
            <p>Local count: {{ count }}</p>
            <button @click="count++">Increment</button>
        </div>
    </template>
    
    <script setup>
    import { ref } from "vue"
    
    const count = ref(0)
    </script>
    

    <template>
        <main>
            <h1>{{ title }}</h1>
            <Counter />
        </main>
    </template>
    
    <script setup>
    import { ref } from "vue"
    import Counter from "./components/Counter.vue"
    
    const title = ref("My Vue App")
    </script>
    


  16. Development vs Production

  17. npm run dev
    

    npm run build
    npm run preview   # optional: preview the production build locally
    


  18. Summary

  19. Step What Happens
    Create project Use npm create vue@latest to scaffold a Vue 3 app
    Entry file main.js imports App.vue and calls createApp(App).mount("#app")
    Root component App.vue defined using Composition API (<script setup>, ref())
    DOM mount point <div id="app"> in index.html
    Components More components go into src/components/ and are imported into App.vue or others
    Dev server npm run dev for live reload while coding
    Production build npm run build to create optimized static files



Template Syntax in Vue

  1. What Is the Template?

  2. <template>
        <h1>{{ title }}</h1>
        <p>Count: {{ count }}</p>
    </template>
    
    <script setup>
    import { ref } from "vue"
    
    const title = ref("Hello from Vue")
    const count = ref(0)
    </script>
    


  3. Text Interpolation with {{ }} (Just like in Angular)

  4. <template>
        <h1>{{ title }}</h1>
        <p>Hello, {{ name }}!</p>
    </template>
    
    <script setup>
    import { ref } from "vue"
    
    const title = ref("Welcome")
    const name = ref("User")
    </script>
    


  5. Using Simple JavaScript Expressions

  6. <template>
        <p>Next: {{ count + 1 }}</p>
        <p>Full name: {{ firstName + " " + lastName }}</p>
        <p>Role: {{ isAdmin ? "Admin" : "User" }}</p>
    </template>
    
    <script setup>
    import { ref } from "vue"
    
    const count     = ref(1)
    const firstName = ref("Junzhe")
    const lastName  = ref("Hwangfu")
    const isAdmin   = ref(false)
    </script>
    


  7. Raw HTML with v-html (Be Careful)

  8. <template>
        <p>Normal: {{ rawHtml }}</p>
        <p v-html="rawHtml"></p>
    </template>
    
    <script setup>
    const rawHtml = "<strong>This is bold</strong>"
    </script>
    


  9. Attribute Binding with v-bind: / :

  10. <template>
        <a :href="profileUrl">Go to profile</a>
        <img :src="avatarUrl" :alt="username" />
    </template>
    
    <script setup>
    const profileUrl = "https://example.com/user/hwangfu"
    const avatarUrl  = "/images/avatar.png"
    const username   = "Hwangfu"
    </script>
    
    <button :disabled="isLoading">
        {{ isLoading ? "Loading..." : "Submit" }}
    </button>
    


  11. Binding class and style

  12. <template>
        <!-- Dynamic classes --
             Vue evaluates the object and produces a normal class string.
             With isActive=true and hasError=false, the final HTML becomes:
             <p class="active">
        -->
        <p :class="{ active: isActive, error: hasError }">
            Status text
        </p>
    
        <!-- Dynamic inline styles --
             Vue converts the object to a real style attribute:
             style="color: blue; font-size: 18px;"
        -->
        <div :style="{ color: textColor, fontSize: size + 'px' }">
            Styled text
        </div>
    </template>
    
    <script setup>
    import { ref } from "vue"
    
    const isActive  = ref(true)
    const hasError  = ref(false)
    
    const textColor = ref("blue")
    const size      = ref(18)
    </script>
    


  13. Conditional Rendering: v-if, v-else-if, v-else, v-show

  14. <template>
        <p v-if="loggedIn">Welcome back!</p>
        <p v-else>Please log in.</p>
    
        <p v-show="showDetails">
            These details can be quickly shown/hidden.
        </p>
    </template>
    
    <script setup>
    import { ref } from "vue"
    
    const loggedIn    = ref(false)
    const showDetails = ref(true)
    </script>
    


  15. List Rendering with v-for and key

  16. <template>
        <ul>
            <li v-for="user in users" :key="user.id">
                {{ user.name }} ({{ user.age }})
            </li>
        </ul>
    </template>
    
    <script setup>
    const users = [
        { id: 1, name: "Alice", age: 23 },
        { id: 2, name: "Bob", age: 31 },
        { id: 3, name: "Charlie", age: 27 }
    ]
    </script>
    


  17. Event Handling with v-on: / @

  18. <template>
        <button @click="increment">
            Clicked {{ count }} times
        </button>
    
        <input
            type="text"
            placeholder="Type something"
            @input="onInput"
        />
    </template>
    
    <script setup>
    import { ref } from "vue"
    
    const count = ref(0)
    
    function increment() {
        count.value++
    }
    
    function onInput(event) {
        console.log("You typed:", event.target.value)
    }
    </script>
    


  19. Two-Way Binding with v-model (Quick Overview)

  20. <template>
        <input v-model="name" placeholder="Your name" />
        <p>Hello, {{ name }}!</p>
    </template>
    
    <script setup>
    import { ref } from "vue"
    
    const name = ref("")
    </script>
    


  21. Directive Shorthands (Cheat Sheet)

  22. Full Form Shorthand Meaning
    v-bind:href="url" :href="url" Bind an attribute to an expression
    v-on:click="doSomething" @click="doSomething" Listen to an event
    v-model="value" none Two-way binding
    v-if="condition" none Conditional rendering
    v-for="item in items" none List rendering



Watchers in Vue (Composition API)

  1. Introduction



  2. watch(): Watching Specific Reactive Sources

  3. import { ref, watch } from "vue"
    
    const name = ref("")
    
    watch(name, (newValue, oldValue) => {
        console.log("Changed:", oldValue, "-->", newValue)
    })
    


  4. Watching Multiple Sources

  5. const first = ref("")
    const last  = ref("")
    
    watch([first, last], ([newFirst, newLast], [oldFirst, oldLast]) => {
        console.log("First:", oldFirst, "-->", newFirst)
        console.log("Last:", oldLast, "-->", newLast)
    })
    


  6. Watching Reactive Objects

  7. import { reactive, watch } from "vue"
    
    const user = reactive({
        name: "Alice",
        age: 25
    })
    
    watch(user, (newVal, oldVal) => {
        console.log("User changed!")
    }, { deep: true })
    


  8. Watching a Getter Function

  9. watch(
        () => user.age,
        (newAge, oldAge) => {
            console.log("Age changed:", oldAge, "-->", newAge)
        }
    )
    


  10. Immediate Watchers

  11. watch(
        count,
        (newVal, oldVal) => {
            console.log("Watcher triggered:", newVal)
        },
        {
            // Run once immediately with the current value
            immediate: true
        }
    )
    


  12. watchEffect(): Automatic Dependency Tracking

  13. import { ref, watchEffect } from "vue"
    
    const count = ref(0)
    
    watchEffect(() => {
        console.log("Count is", count.value)
    })
    


  14. watch vs watchEffect (Comparison)

  15. Feature watch() watchEffect()
    Tracks values Explicitly listed values Automatically detects dependencies
    First run Only when changed (unless immediate) Runs immediately
    Best for side effects on specific values complex reactive logic, debugging
    Old value available? Yes (for refs) No


  16. Cleanup (Stopping Watchers)

  17. const stop = watch(count, () => {
        console.log("count changed")
    })
    
    // Later:
    stop()
    


  18. Practical Example: Fetching from an API

  19. import { ref, watch } from "vue"
    
    const search = ref("")
    const results = ref([])
    
    watch(search, async (term) => {
        if (term.length < 3) {
            results.value = []
            return
        }
    
        const res = await fetch(`/api/search?q=${term}`)
        results.value = await res.json()
    })
    


  20. Summary

  21. Concept Description
    watch() Tracks specific reactive values and gives access to old/new values
    watchEffect() Runs immediately and re-runs whenever any dependency changes
    Deep watch Needed to observe changes inside reactive objects
    Immediate Runs watcher callback right away on initialization
    Usage Side effects such as data fetching, logging, reacting to changes



Template Refs in Vue

  1. Introduction



  2. Basic Example: Referencing a DOM Element

  3. <template>
        <input ref="inputEl" placeholder="Type here..." />
        <button @click="focusInput">Focus input</button>
    </template>
    
    <script setup>
    import { ref } from "vue"
    
    const inputEl = ref(null)
    
    function focusInput() {
        inputEl.value.focus()
    }
    </script>
    


  4. When Is the Template Ref Available?

  5. import { ref, onMounted } from "vue"
    
    const box = ref(null)
    
    onMounted(() => {
        console.log("Box height:", box.value.offsetHeight)
    })
    


  6. Template Refs with Components

  7. <!-- Parent.vue -->
    <template>
        <Child ref="childComp" />
        <button @click="childComp.increment">Call child method</button>
    </template>
    
    <script setup>
    import { ref } from "vue"
    import Child from "./Child.vue"
    
    const childComp = ref(null)
    </script>
    
    <!-- Child.vue -->
    <script setup>
    import { ref } from "vue"
    import { defineExpose } from "vue"
    
    const count = ref(0)
    function increment() { count.value++ }
    
    defineExpose({
        count,
        increment
    })
    </script>
    
    <template>
        <p>{{ count }}</p>
    </template>
    


  8. Binding Refs in v-for

  9. <template>
        <ul>
            <li
                v-for="(user, index) in users"
                :key="user.id"
                :ref="setItemRef"
            >
                {{ user.name }}
            </li>
        </ul>
    </template>
    
    <script setup>
    import { ref } from "vue"
    
    const users = [
        { id: 1, name: "Alice" },
        { id: 2, name: "Bob" }
    ]
    
    const itemRefs = ref([])
    
    function setItemRef(el) {
        if (el) itemRefs.value.push(el)
    }
    </script>
    


  10. Using template ref for Non-DOM Purposes

  11. <template>
        <canvas ref="canvas" width="200" height="200"></canvas>
    </template>
    
    <script setup>
    import { ref, onMounted } from "vue"
    
    const canvas = ref(null)
    
    onMounted(() => {
        const ctx = canvas.value.getContext("2d")
        ctx.fillRect(10, 10, 100, 100)
    })
    </script>
    


  12. Template Ref Caveats



  13. Summary

  14. Concept Description
    DOM refs Use ref="..." to access DOM elements directly
    Component refs Require defineExpose() in the child component
    When available After mount — use onMounted()
    v-for refs Bind a function as :ref to collect multiple elements
    Usage Focus, measurements, canvas, third-party libraries



Using Vue with TypeScript (Vue 3 + Composition API)

  1. Introduction



  2. Enabling TypeScript in a Vue SFC

  3. <script setup lang="ts">
    import { ref } from "vue"
    
    const message = ref("Hello TypeScript")
    </script>
    


  4. Typing Refs

  5. const count = ref(0)          // ref<number>
    const username = ref("Alice") // ref<string>
    
    const age = ref<number | null>(null)
    


  6. Typing Reactive Objects

  7. interface User {
        name: string
        age: number
    }
    
    const user = reactive<User>({
        name: "Hwangfu",
        age: 22
    })
    


  8. Typing Functions Inside Components

  9. function greet(name: string): string {
        return "Hello, " + name
    }
    


  10. Typing Computed Properties

  11. const count = ref(2)
    
    const double = computed(() => count.value * 2)
    // computed<number>
    

    const price = ref(10)
    
    const withTax = computed<number>(() => price.value * 1.19)
    


  12. Typing Props (with <script setup>)

  13. <script setup lang="ts">
    interface Props {
        title: string
        count?: number
    }
    
    const props = defineProps<Props>()
    </script>
    
    <template>
        <h1>{{ props.title }}</h1>
        <p>Count: {{ props.count ?? 0 }}</p>
    </template>
    


  14. Typing Emits

  15. <script setup lang="ts">
    const emit = defineEmits<{
        (event: "update", value: number): void
    }>()
    
    emit("update", 42) // ✔️
    emit("update")     // ❌ type error
    </script>
    


  16. Typing Template Refs

  17. <template>
        <input ref="inputEl" />
    </template>
    
    <script setup lang="ts">
    import { ref, onMounted } from "vue"
    
    const inputEl = ref<HTMLInputElement | null>(null)
    
    onMounted(() => {
        inputEl.value?.focus()
    })
    </script>
    


  18. Typing Composables

  19. // useCounter.ts
    import { ref } from "vue"
    
    export function useCounter(initial = 0) {
        const count = ref(initial)
    
        function increment(): void {
            count.value++
        }
    
        return { count, increment }
    }
    


  20. Typing Events in the Template

  21. <input @input="onInput" />
    
    <script setup lang="ts">
    function onInput(event: Event) {
        const target = event.target as HTMLInputElement
        console.log(target.value)
    }
    </script>
    


  22. Type Safety with defineModel() (Vue 3.4+)

  23. <script setup lang="ts">
    const modelValue = defineModel<string>()
    </script>
    
    <template>
        <input v-model="modelValue" />
    </template>
    


  24. Recommended Project Setup

  25. npm create vue@latest
    # choose: Typescript + JSX support (optional)
    


  26. Summary

  27. Feature TypeScript Usage
    Typing refs ref<T>(initial)
    Reactive objects reactive<Interface>({ ... })
    Props defineProps<Props>()
    Emits defineEmits<Signatures>()
    Template refs ref<ElementType | null>(null)
    Computed auto inferred or computed<T>()
    Composables write typed functions returning reactive state



Components Basics in Vue (Composition API + TypeScript)

  1. Introduction



  2. Your First Component

  3. <!-- Hello.vue -->
    <template>
        <h2>Hello, {{ name }}!</h2>
    </template>
    
    <script setup lang="ts">
    import { defineProps } from "vue";
    
    const props = defineProps<{
        name: string
    }>();
    </script>
    


  4. Using a Component

  5. <!-- App.vue -->
    <template>
        <Hello name="Junzhe" />
    </template>
    
    <script setup lang="ts">
    import Hello from "./components/Hello.vue";
    </script>
    


  6. Props: Passing Data into Components

  7. <script setup lang="ts">
    const props = defineProps<{
        title:  string;
        count?: number;     // optional
    }>()
    </script>
    
    <template>
        <h3>{{ title }}</h3>
        <p>Count: {{ count ?? 0 }}</p>
    </template>
    


  8. Emits: Sending Data from Child to Parent

  9. <!-- Counter.vue -->
    <template>
        <button @click="increment">Count is {{ count }}</button>
    </template>
    
    <script setup lang="ts">
    import { ref, defineEmits } from "vue";
    
    const emit = defineEmits<{
        (e: "update:count", value: number): void;
    }>()
    
    const count = ref(0);
    
    function increment() {
        count.value++;
        emit("update:count", count.value);
    }
    </script>
    
    <!-- Parent.vue -->
    <template>
        <Counter @update:count="val => total = val" />
        <p>Latest count from child: {{ total }}</p>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue";
    import Counter from "./Counter.vue";
    
    const total = ref(0);
    </script>
    


  10. Component Slots: Passing Content into Children

  11. <!-- Card.vue -->
    <template>
        <div class="card">
            <slot /> <!-- default slot -->
        </div>
    </template>
    
    <!-- Parent.vue -->
    <template>
        <Card>
            <p>This is inside the card!</p>
        </Card>
    </template>
    
    <script setup lang="ts">
    import Card from "./Card.vue";
    </script>
    


  12. Named Slots

  13. <!-- Layout.vue -->
    <template>
        <header>
            <slot name="header"/>
        </header>
    
        <main>
            <slot/>
        </main>
    
        <footer>
            <slot name="footer"/>
        </footer>
    </template>
    
    <!-- App.vue -->
    <template>
        <Layout>
            <template #header>Header content</template>
            Main area content
            <template #footer>Footer content</template>
        </Layout>
    </template>
    
    <script setup lang="ts">
    import Layout from "./Layout.vue"
    </script>
    


  14. Component Naming Conventions



  15. Reusable Component Pattern (Full Example)

  16. <!-- Button.vue -->
    <template>
        <button :class="variant" @click="onClick">
            <slot />
        </button>
    </template>
    
    <script setup lang="ts">
    const props = defineProps<{
        variant?: "primary" | "secondary"
    }>()
    
    const emit = defineEmits<{
        (e: "click"): void
    }>()
    
    function onClick() {
        emit("click")
    }
    </script>
    
    <style scoped>
    .primary { padding: 6px; background: #3b82f6; color: white; }
    .secondary { padding: 6px; background: #e5e7eb; }
    </style>
    
    <!-- App.vue -->
    <template>
        <Button variant="primary" @click="sayHi">Say Hi</Button>
    </template>
    
    <script setup lang="ts">
    import Button from "./components/Button.vue"
    
    function sayHi() {
        alert("Hello from Vue!")
    }
    </script>
    


  17. Summary

  18. Feature Description
    Creating components Use .vue SFC files with <script setup lang="ts">
    Props Typed via defineProps<T>() for safe inputs
    Emits Typed events via defineEmits<Signature>()
    Usage Components are imported and used directly in parent templates
    Slots Allow flexible insertion of content
    Composition API + TS Provides best developer experience & type safety



Component Registration in Vue (Global & Local)

  1. Introduction



  2. Local Registration (Recommended)

  3. <!-- Parent.vue -->
    <template>
        <UserCard name="Junzhe" />
    </template>
    
    <script setup lang="ts">
    import UserCard from "./UserCard.vue"
    </script>
    


  4. Local Registration in Old Syntax (Not Needed with <script setup>)

  5. <script lang="ts">
    import { defineComponent } from "vue";
    import UserCard from "./UserCard.vue";
    
    export default defineComponent({
        components: {
            UserCard
        }
    })
    </script>
    


  6. Global Registration

  7. import { createApp } from "vue";
    import App from "./App.vue";
    
    import BaseButton from "./components/BaseButton.vue";
    import BaseCard   from "./components/BaseCard.vue"  ;
    
    const app = createApp(App);
    
    // global registration
    app.component("BaseButton", BaseButton);
    app.component("BaseCard", BaseCard);
    
    app.mount("#app");
    


  8. Automatic Global Registration (Optional)

  9. const components = import.meta.glob("./components/global/*.vue", { eager: true })
    
    for (const path in components) {
        const comp = components[path]
        const name = path.split("/").pop()!.replace(".vue", "")
        app.component(name, (comp as any).default)
    }
    


  10. Local vs Global Registration (Quick Comparison)

  11. Aspect Local Registration Global Registration
    Scope Only usable inside the importing component Usable everywhere in the app
    Recommended? Yes for most components Yes for common UI elements
    Tree-shaking Excellent Potentially worse (less dead-code elimination)
    Setup style Use <script setup> import Register in main.ts
    Best use-case Pages, logic-specific components Buttons, form fields, layouts, icons


  12. Summary

  13. Concept Description
    Local registration Import in <script setup> → immediately available
    Global registration Register with app.component() in main.ts
    Recommended pattern Use local registration for most components
    Global usage Use for shared UI primitives (buttons, cards, icons)
    Automatic loading Possible using import.meta.glob()



Props in Vue (Typed Props Composition API + TypeScript)

  1. Introduction



  2. Defining Typed Props

  3. <!-- UserCard.vue -->
    <template>
        <h2>{{ name }}</h2>
        <p>Age: {{ age }}</p>
    </template>
    
    <script setup lang="ts">
    interface Props {
        name: string
        age: number
    }
    
    const props = defineProps<Props>()
    </script>
    


  4. Passing Props from a Parent Component

  5. <!-- Parent.vue -->
    <template>
        <!--
            Without `:` → the value is a plain string
            With    `:` → the value is a JavaScript expression
        -->
        <UserCard name="Junzhe" :age="22" />
    </template>
    
    <script setup lang="ts">
    import UserCard from "./UserCard.vue"
    </script>
    


  6. Optional Props

  7. const props = defineProps<{
        title: string
        subtitle?: string
    }>()
    


  8. Props with Default Values

  9. <script setup lang="ts">
    const props = withDefaults(
        defineProps<{
            size?: "sm" | "md" | "lg"
            label: string
        }>(),
        {
            size: "md"
        }
    )
    </script>
    


  10. Union Types for Props

  11. const props = defineProps<{
        status: "success" | "warning" | "error"
    }>()
    


  12. Props with Complex Types

  13. interface User {
        id: number
        name: string
        email: string
    }
    
    const props = defineProps<{
        user: User
        tags: string[]
    }>()
    


  14. Readonly Behavior of Props

  15. props.count++   // ❌ not allowed (props are readonly)
    
    const localCount = ref(props.count)
    


  16. Required Props

  17. const props = defineProps<{
        id: number      // required
        name?: string   // optional
    }>()
    


  18. Prop Validation with TypeScript

  19. <!-- ❌ Type error: age must be a number -->
    <UserCard name="Alice" age="twenty" />
    


  20. Destructuring Props (Safe Pattern)

  21. import { toRefs } from "vue"
    
    const props = defineProps<{
        title: string
        count: number
    }>()
    
    const { title, count } = toRefs(props)
    


  22. Props and Emits Together

  23. <!-- Toggle.vue -->
    <template>
        <button @click="toggle">{{ modelValue ? "On" : "Off" }}</button>
    </template>
    
    <script setup lang="ts">
    const props = defineProps<{
        modelValue: boolean
    }>()
    
    const emit = defineEmits<{
        (e: "update:modelValue", value: boolean): void
    }>()
    
    function toggle() {
        emit("update:modelValue", !props.modelValue)
    }
    </script>
    


  24. Best Practices for Props



  25. Summary

  26. Feature Description
    Define props defineProps<T>() with TypeScript interfaces
    Optional props Use ? in type definitions
    Default values Use withDefaults() around defineProps
    Prop immutability Props are readonly — cannot mutate directly
    Complex props Pass objects, arrays, or interfaces
    Destructuring Use toRefs() to keep reactivity
    Type safety TypeScript enforces correct prop usage automatically



Component Events in Vue (Emits + TypeScript)

  1. Introduction



  2. Defining Events with defineEmits()

  3. <!-- Child.vue -->
    <template>
        <button @click="sendMessage">Send</button>
    </template>
    
    <script setup lang="ts">
    const emit = defineEmits<{
        (event: "hello", message: string): void
    }>()
    
    function sendMessage() {
        emit("hello", "Hi from child!")
    }
    </script>
    


  4. Listening to Events in the Parent

  5. <!-- Parent.vue -->
    <template>
        <Child @hello="onHello" />
    </template>
    
    <script setup lang="ts">
    import Child from "./Child.vue"
    
    function onHello(message: string) {
        console.log("Child said:", message)
    }
    </script>
    


  6. Multiple Events

  7. <script setup lang="ts">
    const emit = defineEmits<{
        (e: "increment"): void
        (e: "submit", value: string): void
        (e: "delete", id: number): void
    }>()
    </script>
    


  8. Passing Multiple Arguments

  9. emit("submit", title.value, description.value)
    // Typed as: (e: "submit", title: string, desc: string)
    


  10. Using Events for User Interaction

  11. <!-- LikeButton.vue -->
    <template>
        <button @click="like">Like ({{ count }})</button>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    
    const count = ref(0)
    
    const emit = defineEmits<{
        (e: "liked", newCount: number): void
    }>()
    
    function like() {
        count.value++
        emit("liked", count.value)
    }
    </script>
    
    <!-- Parent.vue -->
    <template>
        <LikeButton @liked="onLiked" />
    </template>
    
    <script setup lang="ts">
    import LikeButton from "./LikeButton.vue"
    
    function onLiked(total: number) {
        console.log("Likes:", total)
    }
    </script>
    


  12. Events and Custom v-model

  13. <!-- Toggle.vue -->
    <template>
        <button @click="toggle">
            {{ modelValue ? "On" : "Off" }}
        </button>
    </template>
    
    <script setup lang="ts">
    const props = defineProps<{
        modelValue: boolean
    }>()
    
    const emit = defineEmits<{
        (e: "update:modelValue", value: boolean): void
    }>()
    
    function toggle() {
        emit("update:modelValue", !props.modelValue)
    }
    </script>
    
    <!-- Parent.vue -->
    <template>
        <!--
            <Toggle
                :modelValue="enabled"
                @update:modelValue="enabled = $event"
            />
        -->
    
        <Toggle v-model="enabled" />
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    import Toggle from "./Toggle.vue"
    
    const enabled = ref(false)
    </script>
    


  14. Typing Event Listeners in Parents

  15. function onSelect(id: number) {
        // id is guaranteed to be a number
    }
    


  16. Event Validation (Old Vue 2 API)

  17. <script setup lang="ts">
    defineEmits({
        submit(payload: string) {
            return payload.length > 0
        }
    })
    </script>
    


  18. Best Practices for Component Events



  19. Summary

  20. Concept Description
    Defining events defineEmits<Signatures>()
    Triggering events emit("eventName", payload)
    Parent listening <Child @eventName="handler" />
    Multiple events Use multiple overload signatures
    Custom v-model Event must be update:modelValue
    Type safety All events strictly typed with TypeScript



Component v-model (Custom Two-Way Binding in Vue)

  1. Introduction



  2. Basic Example: A Simple Toggle Component

  3. <!-- Toggle.vue -->
    <template>
        <button @click="toggle">
            {{ modelValue ? "ON" : "OFF" }}
        </button>
    </template>
    
    <script setup lang="ts">
    const props = defineProps<{
        modelValue: boolean
    }>()
    
    const emit = defineEmits<{
        (e: "update:modelValue", value: boolean): void
    }>()
    
    function toggle() {
        emit("update:modelValue", !props.modelValue)
    }
    </script>
    
    <!-- Parent.vue -->
    <template>
        <Toggle v-model="enabled" />
        <p>Enabled: {{ enabled }}</p>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    import Toggle from "./Toggle.vue"
    
    const enabled = ref(false)
    </script>
    


  4. Understanding the Pattern

  5. Part Purpose
    modelValue The value provided by the parent (v-model)
    update:modelValue The event emitted when the child wants to update the parent
    v-model="x" Links x to modelValue and listens for updates


  6. v-model with TypeScript Validation

  7. const emit = defineEmits<{
        (e: "update:modelValue", value: string): void
    }>()
    


  8. Multiple v-model Bindings

  9. <!-- RangeInput.vue -->
    <template>
        <input type="number" :value="min" @input="onMin($event)" />
        <input type="number" :value="max" @input="onMax($event)" />
    </template>
    
    <script setup lang="ts">
    const props = defineProps<{
        min: number
        max: number
    }>()
    
    const emit = defineEmits<{
        (e: "update:min", value: number): void
        (e: "update:max", value: number): void
    }>()
    
    function onMin(e: Event) {
        emit("update:min", Number((e.target as HTMLInputElement).value))
    }
    
    function onMax(e: Event) {
        emit("update:max", Number((e.target as HTMLInputElement).value))
    }
    </script>
    
    <!-- Parent.vue -->
    <template>
        <RangeInput v-model:min="start" v-model:max="end" />
        <p>Range: {{ start }} - {{ end }}</p>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    import RangeInput from "./RangeInput.vue"
    
    const start = ref(1)
    const end = ref(10)
    </script>
    


  10. Customizing v-model Name

  11. <!-- SearchInput.vue -->
    <script setup lang="ts">
    const props = defineProps<{ query: string }>()
    
    const emit = defineEmits<{
        (e: "update:query", value: string): void
    }>()
    </script>
    
    <template>
        <input :value="query" @input="emit('update:query', $event.target.value)" />
    </template>
    
    <!-- Parent.vue -->
    <template>
        <SearchInput v-model:query="searchText" />
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    import SearchInput from "./SearchInput.vue"
    
    const searchText = ref("")
    </script>
    


  12. Using Local State + Props in v-model

  13. <script setup lang="ts">
    import { ref, watch } from "vue"
    
    const props = defineProps<{ modelValue: string }>()
    const emit = defineEmits<{ (e: "update:modelValue", value: string): void }>()
    
    const internal = ref(props.modelValue)
    
    // sync internal state to prop changes
    watch(() => props.modelValue, (v) => internal.value = v)
    
    function update(v: string) {
        internal.value = v
        emit("update:modelValue", v)
    }
    </script>
    


  14. Best Practices



  15. Summary

  16. Feature Description
    Default v-model Uses modelValue prop + update:modelValue event
    Multiple v-models Use v-model:name + update:name
    Type safety All payloads validated by TypeScript
    Local state pattern Use ref() + watch() for more control
    Component usage <CustomInput v-model="value" />



Fallthrough Attributes in Vue (Composition API + TypeScript)

  1. Introduction



  2. Basic Example of Fallthrough Attributes

  3. <!-- Button.vue -->
    <template>
        <button>
            <slot />
        </button>
    </template>
    
    <script setup lang="ts">
    </script>
    
    <!-- Parent.vue -->
    <template>
        <Button id="main-btn" class="primary" data-x="123">
            Click me
        </Button>
    </template>
    
    <button id="main-btn" class="primary" data-x="123">
        Click me
    </button>
    


  4. Accessing Fallthrough Attributes

  5. <script setup lang="ts">
    import { useAttrs } from "vue"
    
    const attrs = useAttrs()
    
    console.log(attrs)
    // { id: "main-btn", class: "primary", "data-x": "123" }
    </script>
    


  6. Manually Applying Fallthrough Attributes

  7. <!-- InputWrapper.vue -->
    <template>
        <label>
            {{ label }}
            <input v-bind="attrs" />
        </label>
    </template>
    
    <script setup lang="ts">
    import { defineProps, useAttrs } from "vue"
    
    const props = defineProps<{
        label: string
    }>()
    
    const attrs = useAttrs()
    </script>
    


  8. Disabling Automatic Fallthrough

  9. <!-- CustomField.vue -->
    <script setup lang="ts">
    defineOptions({
        inheritAttrs: false
    })
    </script>
    
    <template>
        <div>
            <!-- No automatic fallthrough onto this element -->
            <slot />
        </div>
    </template>
    


  10. Fallthrough and Multiple Root Elements

  11. <!-- Wrong: Multi-root component -->
    <template>
        <h1>Hello</h1>
        <p>World</p>
    </template>
    
    <!-- Correct -->
    <template>
        <div v-bind="$attrs">
            <h1>Hello</h1>
            <p>World</p>
        </div>
    </template>
    


  12. Fallthrough Attributes with TypeScript

  13. const attrs = useAttrs() as Record<string, string | number | boolean>
    


  14. Common Use Cases



  15. Best Practices



  16. Summary

  17. Concept Description
    Fallthrough attributes Attributes not declared as props automatically applied to the component root
    $attrs / useAttrs() Access fallthrough attributes inside the component
    inheritAttrs: false Disables automatic fallthrough behavior
    Manual binding v-bind="$attrs" applies attributes to chosen elements
    Multiple roots Require manual placement of fallthrough attributes



Lifecycle Hooks in Vue (Composition API + TypeScript)

  1. Introduction



  2. Basic Example

  3. <script setup lang="ts">
    import { onMounted, onUnmounted } from "vue"
    
    onMounted(() => {
        console.log("Component mounted!")
    })
    
    onUnmounted(() => {
        console.log("Component removed!")
    })
    </script>
    


  4. Full List of Composition API Lifecycle Hooks

  5. Hook When It Runs
    onBeforeMount Right before the component is mounted to the DOM
    onMounted After the component is mounted (DOM is available)
    onBeforeUpdate Before the component re-renders due to reactive changes
    onUpdated After the DOM is updated
    onBeforeUnmount Right before the component is removed
    onUnmounted After the component is removed from the DOM
    onErrorCaptured When an error occurs in a child component
    onRenderTracked Tracks reactive dependencies during render (debug only)
    onRenderTriggered Called when reactive dependencies cause re-render (debug only)
    onActivated When a component wrapped by <KeepAlive> becomes active
    onDeactivated When a kept-alive component is deactivated


  6. onMounted() — When DOM Is Available

  7. onMounted(() => {
        console.log("Component ready!")
    })


  8. onBeforeMount() — Before Mounting

  9. onBeforeMount(() => {
        console.log("About to mount...")
    })


  10. onUpdated() and onBeforeUpdate()

  11. onBeforeUpdate(() => {
        console.log("DOM is about to update")
    })
    
    onUpdated(() => {
        console.log("DOM updated")
    })
    


  12. onUnmounted() — Cleanup Logic

  13. onUnmounted(() => {
        console.log("Component destroyed")
    })


  14. Handling Errors with onErrorCaptured()

  15. onErrorCaptured((err, instance, info) => {
        console.error("Error:", err)
        return false   // prevents further propagation
    })


  16. Debug Hooks: onRenderTracked() & onRenderTriggered()

  17. onRenderTracked((event) => {
        console.log("Tracked:", event)
    })
    
    onRenderTriggered((event) => {
        console.log("Triggered:", event)
    })


  18. KeepAlive Hooks: onActivated() & onDeactivated()

  19. <KeepAlive>
        <MyComponent />
    </KeepAlive>
    onActivated(() => console.log("Activated"))
    onDeactivated(() => console.log("Deactivated"))


  20. Using Lifecycle Hooks with TypeScript

  21. onMounted((): void => {
        console.log("Typed hook example")
    })


  22. Summary

  23. Hook Purpose
    onMounted Run code after initial render (DOM ready)
    onUnmounted Cleanup when component is removed
    onUpdated Run code after DOM updates
    onBeforeMount Before initial DOM insertion
    onBeforeUpdate Before DOM updates
    onErrorCaptured Error handling from descendants
    onActivated / onDeactivated Hooks for <KeepAlive> components



Slots in Vue (Composition API + TypeScript)


  1. Slots allow parent components to pass template content into child components.


  2. Basic Slot

  3. <!-- Child.vue -->
    <template>
        <div class="box">
            <slot>Fallback content</slot>
        </div>
    </template>
    
    <script setup lang="ts"></script>
    

    <!-- Parent.vue -->
    <Child>
        <p>This is inserted into the slot!</p>
    </Child>


  4. Named Slots

  5. <!-- Card.vue -->
    <template>
        <div class="card">
            <header><slot name="header" /></header>
            <main><slot /></main>
            <footer><slot name="footer" /></footer>
        </div>
    </template>
    
    <script setup lang="ts"></script>
    

    <Card>
        <template #header>Header Content</template>
        Body text goes here.
        <template #footer>Footer Content</template>
    </Card>
    


  6. Scoped Slots (Passing Data From Child to Parent)

  7. <!-- UserList.vue -->
    <template>
        <ul>
            <li v-for="user in users" :key="user.id">
                <slot :user="user">{{ user.name }}</slot>
            </li>
        </ul>
    </template>
    
    <script setup lang="ts">
    interface User {
        id: number;
        name: string;
    }
    
    const users: User[] = [
        { id: 1, name: "Alice" },
        { id: 2, name: "Bob" }
    ];
    </script>
    

    <UserList>
        <template #default="{ user }">
            <strong>{{ user.name }}</strong>
        </template>
    </UserList>
    


  8. Typing Slot Props in TypeScript

  9. <script setup lang="ts">
    import type { SlotsType } from "vue"
    
    interface User {
        id: number;
        name: string;
    }
    
    defineSlots<SlotsType<{
        default: { user: User }
    }>>()
    </script>
    


  10. Default Slot + Named Slots Together

  11. <!-- Layout.vue -->
    <template>
        <div class="layout">
            <aside><slot name="sidebar" /></aside>
            <section><slot /></section>
        </div>
    </template>
    
    <script setup lang="ts"></script>
    
    <Layout>
        <template #sidebar>Menu items...</template>
        <p>Main content goes here.</p>
    </Layout>
    


  12. Fallback Content

  13. <slot>Nothing provided</slot>


  14. Summary

  15. Feature Description
    Default slot Main content placeholder
    Named slots Multiple content areas such as header/footer/sidebar
    Scoped slots Child passes data to parent templates
    Fallback content Shown when no parent content is provided
    TypeScript slots Use defineSlots for typing slot props



Conditional Rendering in Vue (Composition API + TypeScript)

  1. Introduction



  2. v-if — Render Only If Condition Is True

  3. <template>
        <div>
            <p v-if="loggedIn">Welcome back!</p>
            <p v-else>Please log in.</p>
        </div>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    
    const loggedIn = ref(false)
    </script>
    


  4. v-else-if and v-else

  5. <template>
        <div>
            <p v-if="score >= 90">Excellent</p>
            <p v-else-if="score >= 60">Pass</p>
            <p v-else>Fail</p>
        </div>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    
    const score = ref(75)
    </script>
    


  6. v-show — Toggle Visibility with CSS

  7. <template>
        <button @click="show = !show">Toggle</button>
        <p v-show="show">I stay in the DOM.</p>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    
    const show = ref(true)
    </script>
    


  8. v-if vs v-show: When to Use Which?

  9. Directive Behavior Best Use Case
    v-if Creates/removes DOM elements Rare toggles, expensive components
    v-show Always renders, toggles visibility via CSS Frequent toggling, always needed in DOM


  10. Conditional Groups Using <template>

  11. <template v-if="user">
        <h2>User Info</h2>
        <p>Name: {{ user.name }}</p>
    </template>
    
    <template v-else>
        <p>No user found.</p>
    </template>
    
    <script setup lang="ts">
    interface User {
        name: string;
    }
    
    const user = ref<User | null>({ name: "Alice" })
    </script>
    


  12. Using Computed Properties for Cleaner Conditions

  13. <script setup lang="ts">
    import { ref, computed } from "vue"
    
    const age = ref(20)
    
    const isAdult = computed(() => age.value >= 18)
    </script>
    
    <template>
        <p v-if="isAdult">You are an adult.</p>
    </template>
    


  14. v-if with Async Data (Common Pattern)

  15. <template>
        <p v-if="loading">Loading...</p>
        <p v-else-if="error">Error: {{ error }}</p>
        <p v-else>Data: {{ data }}</p>
    </template>
    
    <script setup lang="ts">
    import { ref, onMounted } from "vue"
    
    const loading = ref(true)
    const error = ref<string | null>(null)
    const data = ref<string | null>(null)
    
    onMounted(async () => {
        try {
            const res = await fetch("/api/message")
            data.value = await res.text()
        } catch (err) {
            error.value = String(err)
        } finally {
            loading.value = false
        }
    })
    </script>
    


  16. Best Practices



  17. Summary

  18. Directive Purpose
    v-if Conditionally render elements (added/removed from DOM)
    v-else-if / v-else Chain conditional rendering
    v-show Toggle visibility using CSS, always in DOM
    v-if + <template> Conditionally render a block of elements
    Computed conditions Cleaner and reusable conditional logic



List Rendering in Vue (Composition API + TypeScript)

  1. Introduction



  2. Basic List Rendering with Arrays

  3. <template>
        <ul>
            <li v-for="name in names" :key="name">
                {{ name }}
            </li>
        </ul>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    
    const names = ref<string[]>(["Alice", "Bob", "Charlie"])
    </script>
    


  4. Accessing Index in v-for
  5. <li v-for="(name, index) in names" :key="index">
        {{ index }} - {{ name }}
    </li>
    


  6. Rendering a List of Objects
  7. <template>
        <ul>
            <li v-for="user in users" :key="user.id">
                {{ user.name }} ({{ user.age }})
            </li>
        </ul>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    
    interface User {
        id: number
        name: string
        age: number
    }
    
    const users = ref<User[]>([
        { id: 1, name: "Alice", age: 20 },
        { id: 2, name: "Bob", age: 25 }
    ])
    </script>
    


  8. Using Computed Properties to Transform Lists
  9. <script setup lang="ts">
    import { ref, computed } from "vue"
    
    const numbers = ref([1, 2, 3, 4, 5])
    
    const evenNumbers = computed(() =>
        numbers.value.filter(n => n % 2 === 0)
    )
    </script>
    
    <template>
        <p v-for="num in evenNumbers" :key="num">
            {{ num }}
        </p>
    </template>
    


  10. v-for on <template> (Multiple Elements)

  11. <template v-for="user in users" :key="user.id">
        <h3>{{ user.name }}</h3>
        <p>Age: {{ user.age }}</p>
    </template>
    


  12. Looping Through Objects
  13. <template>
        <div v-for="(value, key) in settings" :key="key">
            {{ key }}: {{ value }}
        </div>
    </template>
    
    <script setup lang="ts">
    import { reactive } from "vue"
    
    const settings = reactive({
        theme: "dark",
        notifications: true,
        version: 3
    })
    </script>
    


  14. Looping Through a Range (v-for with Numbers)
  15. <li v-for="n in 5" :key="n">
        Item {{ n }}
    </li>
    


  16. Combining v-for with v-if (Warning)

  17. <!-- ❌ Avoid -->
    <li v-for="user in users" v-if="user.active" :key="user.id">
        {{ user.name }}
    </li>
    
    <!-- ✔ Better -->
    <li v-for="user in activeUsers" :key="user.id">
        {{ user.name }}
    </li>
    
    <script setup lang="ts">
    import { ref, computed } from "vue"
    
    const users = ref([
        { id: 1, name: "Alice", active: true },
        { id: 2, name: "Bob", active: false }
    ])
    
    const activeUsers = computed(() =>
        users.value.filter(u => u.active)
    )
    </script>
    


  18. Tracking List Items with Unique Keys

  19. <li v-for="todo in todos" :key="todo.id">
        {{ todo.text }}
    </li>
    


  20. Using List Rendering with Components

  21. <template>
        <TodoItem
            v-for="todo in todos"
            :key="todo.id"
            :todo="todo"
        />
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    import TodoItem from "./TodoItem.vue"
    
    const todos = ref([
        { id: 1, text: "Study Vue" },
        { id: 2, text: "Write components" }
    ])
    </script>
    


  22. Summary

  23. Feature Description
    v-for Loop through arrays, objects, or numbers
    :key Helps Vue track elements efficiently
    Computed filtering Recommended instead of using v-if inside v-for
    Object iteration Access value + key
    Range iteration Loop n times using v-for="n in 10"
    Component lists Use v-for with components and properly typed props



Event Handling in Vue (Composition API + TypeScript)

  1. Introduction



  2. Basic Event Listening

  3. <template>
        <button @click="increment">Count: {{ count }}</button>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    
    const count = ref(0)
    
    function increment() {
        count.value++
    }
    </script>
    


  4. Passing Arguments to Event Handlers

  5. <button @click="add(5)">+5</button>
    
    <script setup lang="ts">
    function add(amount: number) {
        console.log("Added:", amount)
    }
    </script>
    


  6. Accessing the Native Event Object

  7. <input @input="handleInput($event)" />
    
    <script setup lang="ts">
    function handleInput(e: Event) {
        const target = e.target as HTMLInputElement
        console.log(target.value)
    }
    </script>
    


  8. Inline Arrow Functions
  9. <button @click="() => count++">Increment</button>
    


  10. Event Modifiers

  11. Modifier Meaning
    .stop Calls event.stopPropagation()
    .prevent Calls event.preventDefault()
    .capture Use capture mode
    .once Listener runs only once
    .passive Use passive mode (scroll performance)

    <button @click.stop="doSomething">Stop Propagation</button>
    <form @submit.prevent="submit">...</form>
    


  12. Key Modifiers (Keyboard Events)

  13. <input @keyup.enter="submit" />
    

    <input @keyup.ctrl.enter="save" />
    


  14. Mouse Button Modifiers
  15. <button @click.right="onRightClick">Right Click</button>
    <button @click.middle="onMiddleClick">Middle Click</button>
    


  16. v-on on <template>

  17. <template @click="clicked">
        <button>A</button>
        <button>B</button>
    </template>
    


  18. Event Handling with TypeScript (Strong Typing)

  19. <script setup lang="ts">
    function onInput(e: Event) {
        const target = e.target as HTMLInputElement
        console.log(target.value)
    }
    
    function onClick(e: MouseEvent) {
        console.log("Mouse X:", e.clientX)
    }
    </script>
    


  20. Using Events Inside Components
  21. <ChildComponent @update="handleUpdate" />
    
    <script setup lang="ts">
    function handleUpdate(value: number) {
        console.log("Updated:", value)
    }
    </script>
    



  22. Summary

  23. Feature Description
    @click, @input, etc. Attach DOM event listeners
    Event modifiers .stop, .prevent, .once, etc.
    Key modifiers .enter, .esc, .ctrl.enter, etc.
    Inline event handlers Quick logic but less readable
    Typed events Use TypeScript for safe event handling



Form Input Bindings in Vue (Composition API + TypeScript)

  1. Introduction



  2. Text Input with v-model
  3. <template>
        <input v-model="name" placeholder="Your name" />
        <p>Hello, {{ name }}!</p>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    
    const name = ref("")
    </script>
    


  4. Textarea
  5. <textarea v-model="message"></textarea>
    
    <script setup lang="ts">
    const message = ref("")
    </script>
    


  6. Checkbox (Boolean)

  7. <input type="checkbox" v-model="isChecked" />
    <p>Checked: {{ isChecked }}</p>
    
    <script setup lang="ts">
    const isChecked = ref(false)
    </script>
    


  8. Checkbox Group (Array Binding)

  9. <template>
        <label>
            <input type="checkbox" value="apple" v-model="fruits" /> Apple
        </label>
        <label>
            <input type="checkbox" value="banana" v-model="fruits" /> Banana
        </label>
    
        <p>Selected: {{ fruits }}</p>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    
    const fruits = ref<string[]>([])
    </script>
    


  10. Radio Buttons

  11. <template>
        <label>
            <input type="radio" value="male" v-model="gender" /> Male
        </label>
        <label>
            <input type="radio" value="female" v-model="gender" /> Female
        </label>
    
        <p>Gender: {{ gender }}</p>
    </template>
    
    <script setup lang="ts">
    const gender = ref("male")
    </script>
    


  12. Select Dropdown (Single)
  13. <select v-model="city">
        <option value="Berlin">Berlin</option>
        <option value="Munich">Munich</option>
        <option value="Hamburg">Hamburg</option>
    </select>
    
    <script setup lang="ts">
    const city = ref("Berlin")
    </script>
    


  14. Select Dropdown (Multiple)
  15. <select v-model="cities" multiple>
        <option value="Berlin">Berlin</option>
        <option value="Munich">Munich</option>
        <option value="Hamburg">Hamburg</option>
    </select>
    
    <script setup lang="ts">
    const cities = ref<string[]>([])
    </script>
    


  16. Binding Value Types (Number / Boolean / String)

  17. <input v-model.number="age" type="number" />
    
    <script setup lang="ts">
    const age = ref<number | null>(null)
    </script>
    

    <input v-model.trim="username" />
    <input v-model.lazy="email" />
    


  18. Binding to reactive() Objects

  19. <template>
        <input v-model="form.email" placeholder="Email" />
        <input type="password" v-model="form.password" placeholder="Password" />
    </template>
    
    <script setup lang="ts">
    import { reactive } from "vue"
    
    const form = reactive({
        email: "",
        password: ""
    })
    </script>
    


  20. TypeScript Typing for Forms
  21. <script setup lang="ts">
    import { reactive } from "vue"
    
    interface LoginForm {
        email: string
        password: string
        remember: boolean
    }
    
    const form = reactive<LoginForm>({
        email: "",
        password: "",
        remember: false
    })
    </script>
    


  22. Using v-model with Components

  23. <CustomInput v-model="username" />
    


  24. Best Practices



  25. Summary

  26. Feature Description
    v-model Two-way binding for form fields
    Modifiers .trim, .number, .lazy
    Checkbox & Radio Boolean, array, or single-value binding
    Select Single or multiple selection support
    reactive() Organize form data in typed objects
    TypeScript support Strong typing for safe form handling



Scoped Slots in Vue (Composition API + TypeScript)

  1. Introduction



  2. How Scoped Slots Work (Concept)

  3. <slot :user="user"></slot>

    <template #default="{ user }">
        {{ user.name }}
    </template>
    


  4. Basic Scoped Slot Example

  5. <!-- UserList.vue -->
    <template>
        <ul>
            <li v-for="user in users" :key="user.id">
                <slot :user="user">
                    {{ user.name }}
                </slot>
            </li>
        </ul>
    </template>
    
    <script setup lang="ts">
    interface User {
        id: number
        name: string
    }
    
    const users: User[] = [
        { id: 1, name: "Alice" },
        { id: 2, name: "Bob" }
    ]
    </script>
    

    <UserList>
        <template #default="{ user }">
            <strong>{{ user.name.toUpperCase() }}</strong>
        </template>
    </UserList>
    


  6. Example: Building a Flexible Table

  7. <!-- DataTable.vue -->
    <template>
        <table>
            <tr v-for="row in rows" :key="row.id">
                <slot :row="row"></slot>
            </tr>
        </table>
    </template>
    
    <script setup lang="ts">
    interface Row {
        id: number
        name: string
        age: number
    }
    
    const rows: Row[] = [
        { id: 1, name: "Alice", age: 20 },
        { id: 2, name: "Bob", age: 25 }
    ]
    </script>
    
    <DataTable>
        <template #default="{ row }">
            <td>{{ row.name }}</td>
            <td>{{ row.age }}</td>
        </template>
    </DataTable>
    


  8. Typing Scoped Slots in TypeScript (Strong Typing)


  9. <!-- UserCard.vue -->
    <template>
        <div class="card">
            <!-- Expose "user" as a scoped slot prop to the parent -->
            <slot :user="user"></slot>
        </div>
    </template>
    
    <script setup lang="ts">
    import type { SlotsType } from "vue"
    
    interface User {
        id: number
        name: string
    }
    
    const user: User = {
        id: 1,
        name: "Alice"
    }
    
    defineSlots<SlotsType<{
        default: { user: User }
    }>>()
    </script>
    
    <!-- Parent.vue -->
    <template>
        <UserCard>
            <template #default="{ user }">
                <p>{{ user.name }} (ID: {{ user.id }})</p>
            </template>
        </UserCard>
    </template>
    
    <script setup lang="ts">
    import UserCard from "./UserCard.vue"
    </script>
    


    <!-- Panel.vue -->
    <template>
        <section class="panel">
            <header>
                <slot name="header" :title="title"></slot>
            </header>
    
            <main>
                <slot :item="item"></slot>
            </main>
    
            <footer>
                <slot name="footer" :updatedAt="updatedAt"></slot>
            </footer>
        </section>
    </template>
    
    <script setup lang="ts">
    import type { SlotsType } from "vue"
    
    const title = "Panel Title"
    const item = "Some item"
    const updatedAt = new Date()
    
    defineSlots<SlotsType<{
        header: { title: string }
        default: { item: string }
        footer: { updatedAt: Date }
    }>>()
    </script>
    
    <!-- App.vue -->
    <template>
        <Panel>
            <template #header="{ title }">
                <h2> {{ title }} </h2>
            </template>
    
            <template #default="{ item }">
                <p>Item: {{ item }}</p>
            </template>
    
            <template #footer="{ updatedAt }">
                <small>Last updated: {{ updatedAt.toLocaleString() }}</small>
            </template>
        </Panel>
    </template>
    
    <script setup lang="ts">
    import Panel from "./Panel.vue"
    </script>
    


    <script setup lang="ts">
    import type { SlotsType } from "vue"
    
    interface User {
        id: number
        name: string
    }
    
    const user: User | null = null
    
    defineSlots<SlotsType<{
        default: { user?: User | null }
    }>>()
    </script>
    


  10. Multiple Scoped Slots

  11. <!-- UserCard.vue -->
    <template>
        <div class="card">
            <slot name="header" :user="user" />
            <p>Age: {{ user.age }}</p>
            <slot name="footer" :user="user" />
        </div>
    </template>
    
    <script setup lang="ts">
    const user = {
        name: "Alice",
        age: 22
    }
    </script>
    
    <UserCard>
        <template #header="{ user }">
            <h3>{{ user.name }}</h3>
        </template>
    
        <template #footer="{ user }">
            <small>User is {{ user.age }} years old.</small>
        </template>
    </UserCard>
    


  12. Default Slot Fallback Behavior

  13. <slot :item="item">Default layout for {{ item.name }}</slot>
    


  14. Advanced: Passing Functions via Scoped Slots

  15. <!-- ActionList.vue -->
    <template>
        <slot :remove="remove" :items="items"></slot>
    </template>
    
    <script setup lang="ts">
    import { ref } from "vue"
    
    const items = ref(["A", "B", "C"])
    
    function remove(index: number) {
        items.value.splice(index, 1)
    }
    </script>
    
    <ActionList>
        <template #default="{ items, remove }">
            <div v-for="(item, i) in items" :key="item">
                {{ item }}
                <button @click="remove(i)">X</button>
            </div>
        </template>
    </ActionList>
    


  16. Summary

  17. Concept Description
    Scoped Slots Child exposes data → parent renders UI
    Data Direction Child → Parent
    Usage Lists, tables, cards, customized layouts
    TypeScript Support Use defineSlots + SlotsType
    Named Scoped Slots Useful for components with multiple regions
    Fallback Content Default slot content if parent provides none



Provide / Inject in Vue (Composition API + TypeScript)

  1. Introduction



  2. Basic Usage

  3. <!-- Parent.vue -->
    <template>
        <div>
            <h2>Parent</h2>
            <Child />
        </div>
    </template>
    
    <script setup lang="ts">
    import { provide } from "vue"
    import Child from "./Child.vue"
    
    const message = "Hello from Parent"
    
    provide("msg", message)  // key = "msg"
    </script>
    
    <!-- Child.vue -->
    <template>
        <p>Injected: {{ msg }}</p>
    </template>
    
    <script setup lang="ts">
    import { inject } from "vue"
    
    const msg = inject("msg")
    </script>
    

    const msg = inject("msg", "default value")
    


  4. Typed Provide / Inject Using Injection Keys

  5. // keys.ts
    import type { InjectionKey } from "vue"
    
    export const userKey: InjectionKey<string> = Symbol("userKey")
    
    <!-- Parent.vue -->
    <script setup lang="ts">
    import { provide } from "vue"
    import { userKey } from "./keys"
    
    provide(userKey, "Alice")
    </script>
    
    <!-- Child.vue -->
    <script setup lang="ts">
    import { inject } from "vue"
    import { userKey } from "./keys"
    
    const user = inject(userKey)
    </script>
    

    const user = inject(userKey, "Guest")
    


  6. Providing Reactive Values

  7. <!-- Parent.vue -->
    <script setup lang="ts">
    import { ref, provide } from "vue"
    import { countKey } from "./keys"
    
    const count = ref(0)
    
    provide(countKey, count)
    </script>
    
    <!-- Child.vue -->
    <script setup lang="ts">
    import { inject } from "vue"
    import { countKey } from "./keys"
    
    const count = inject(countKey)
    </script>
    
    <template>
        <button @click="count.value++">Increment</button>
        <p>Count: {{ count.value }}</p>
    </template>
    


  8. Providing Complex Objects
  9. <!-- keys.ts -->
    import type { InjectionKey } from "vue"
    
    export interface AuthContext {
        user: string
        login: (name: string) => void
        logout: () => void
    }
    
    export const authKey: InjectionKey<AuthContext> = Symbol("authKey")
    

    <script setup lang="ts">
    import { provide, ref } from "vue"
    import { authKey } from "./keys"
    
    const user = ref("Guest")
    
    function login(name: string) {
        user.value = name
    }
    
    function logout() {
        user.value = "Guest"
    }
    
    provide(authKey, { user, login, logout })
    </script>
    
    <template>
        <slot />
    </template>
    

    <script setup lang="ts">
    import { inject } from "vue"
    import { authKey } from "./keys"
    
    const auth = inject(authKey)
    
    if (!auth) throw new Error("AuthProvider is missing")
    </script>
    
    <template>
        <p>Current user: {{ auth.user }}</p>
        <button @click="auth.login('Alice')">Login as Alice</button>
    </template>
    


  10. Default Values

  11. const config = inject(configKey, { theme: "light" })
    


  12. Summary

  13. Concept Description
    provide() Defines shared data from an ancestor component
    inject() Consumes shared data in any descendant
    TypeScript keys Use InjectionKey<T> + Symbol for type-safe injection
    Reactivity Reactive values stay reactive across components
    Use cases Global config, services, shared stores, avoiding prop drilling



Async Components in Vue (Composition API + TypeScript)

  1. Introduction



  2. Basic Async Component

  3. <!-- Parent.vue -->
    <template>
        <AsyncChild />
    </template>
    
    <script setup lang="ts">
    import { defineAsyncComponent } from "vue"
    
    const AsyncChild = defineAsyncComponent(() =>
        import("./Child.vue")
    )
    </script>
    


  4. Typing Async Components

  5. <!-- Parent.vue -->
    <script setup lang="ts">
    import { defineAsyncComponent } from "vue"
    import type { DefineComponent } from "vue"
    
    const AsyncForm = defineAsyncComponent<DefineComponent>(() =>
        import("./Form.vue")
    )
    </script>
    


  6. Async Component with Loading and Error Components

  7. <!-- Parent.vue -->
    <script setup lang="ts">
    import { defineAsyncComponent } from "vue"
    
    const AsyncChart = defineAsyncComponent({
        loader: () => import("./Chart.vue"),
        loadingComponent: () => import("./Loading.vue"),
        errorComponent: () => import("./Error.vue"),
        delay: 200,     // show loading component after 200ms
        timeout: 3000   // throw error if it takes >3s
    })
    </script>
    
    <template>
        <AsyncChart />
    </template>
    


  8. Using Async Components with <Suspense>

  9. <!-- Parent.vue -->
    <template>
        <Suspense>
            <template #default>
                <AsyncProfile />
            </template>
    
            <template #fallback>
                <p>Loading profile...</p>
            </template>
        </Suspense>
    </template>
    
    <script setup lang="ts">
    import { defineAsyncComponent } from "vue"
    
    const AsyncProfile = defineAsyncComponent(() =>
        import("./Profile.vue")
    )
    </script>
    


  10. Dynamic Async Components

  11. <!-- Parent.vue -->
    <template>
        <component :is="currentView" />
    
        <button @click="showLogin">Login</button>
        <button @click="showDashboard">Dashboard</button>
    </template>
    
    <script setup lang="ts">
    import { ref, defineAsyncComponent } from "vue"
    
    const LoginView     = defineAsyncComponent(() => import("./Login.vue"))
    const DashboardView = defineAsyncComponent(() => import("./Dashboard.vue"))
    
    const currentView = ref(LoginView)
    
    function showLogin() {
        currentView.value = LoginView
    }
    function showDashboard() {
        currentView.value = DashboardView
    }
    </script>
    


  12. Preloading Async Components

  13. <script setup lang="ts">
    import { defineAsyncComponent } from "vue"
    
    // Preload early
    import("./HeavyPanel.vue")
    
    // Create async component after preload
    const HeavyPanel = defineAsyncComponent(() => import("./HeavyPanel.vue"))
    </script>
    


  14. Summary

  15. Feature Description
    Lazy loading Load components only when needed
    defineAsyncComponent Main API for creating async components
    Loading/error UI Custom components for loading states and errors
    <Suspense> Simplified loading fallback system
    TypeScript support Use DefineComponent to type async imports
    Dynamic switching Use <component :is="..."> with async components



Composables in Vue

  1. What Are Composables in Vue?



  2. Basic Example: A Counter Composable

  3. src/
    └── composables/
        └── useCounter.ts
    
    import { ref } from "vue";
    
    export function useCounter() {
        const count = ref(0);
    
        function increment() {
            count.value++;
        }
    
        function decrement() {
            count.value--;
        }
    
        return { count, increment, decrement };
    }
    
    <script setup lang="ts">
    import { useCounter } from "@/composables/useCounter";
    
    const { count, increment, decrement } = useCounter();
    </script>
    
    <template>
        <button @click="decrement">-</button>
        {{ count }}
        <button @click="increment">+</button>
    </template>
    


  4. Example: Fetch Data Composable

  5. import { ref, onMounted } from "vue";
    
    export function useFetch(url: string) {
        const data    = ref(null);
        const loading = ref(true);
        const error   = ref(null);
    
        async function load() {
            loading.value = true;
            try {
                const res = await fetch(url);
                data.value = await res.json();
            } catch (err: any) {
                error.value = err.message;
            } finally {
                loading.value = false;
            }
        }
    
        onMounted(load);
    
        return { data, loading, error, load };
    }
    
    <script setup lang="ts">
    import { useFetch } from "@/composables/useFetch";
    
    const { data, loading, error } = useFetch("https://api.example.com/posts");
    </script>
    
    <template>
        <p v-if="loading">Loading...</p>
        <p v-if="error">Error: {{ error }}</p>
        <pre v-if="data">{{ data }}</pre>
    </template>
    


  6. Rules of Composables



  7. Example: Shared Global State (Simple Store)

  8. import { ref } from "vue";
    
    const dark = ref(false);  // <-- shared across all components
    
    export function useDarkMode() {
        function toggle() {
            dark.value = !dark.value;
        }
    
        return { dark, toggle };
    }
    
    const { dark, toggle } = useDarkMode();
    
    const { dark } = useDarkMode();
    


  9. Example: Listening to Window Resize

  10. import { ref, onMounted, onUnmounted } from "vue";
    
    export function useWindowSize() {
        const width = ref(window.innerWidth);
        const height = ref(window.innerHeight);
    
        function update() {
            width.value = window.innerWidth;
            height.value = window.innerHeight;
        }
    
        onMounted(() => window.addEventListener("resize", update));
        onUnmounted(() => window.removeEventListener("resize", update));
    
        return { width, height };
    }
    
    <template>
        Width: {{ width }}
        Height: {{ height }}
    </template>
    
    <script setup lang="ts">
    import { useWindowSize } from "@/composables/useWindowSize";
    
    const { width, height } = useWindowSize();
    </script>
    


  11. Folder Organization (Recommended)

  12. src/
    ├── composables/
    │   ├── useCounter.ts
    │   ├── useFetch.ts
    │   ├── useDarkMode.ts
    │   ├── useWindowSize.ts
    │   └── useForm.ts
    ├── components/
    ├── pages/
    └── ...
    



Custom Directives in Vue (Composition API + TypeScript)

  1. What Are Directives in Vue?



  2. Directive Lifecycle Hooks (TypeScript)
  3. import type { Directive, DirectiveBinding } from "vue";
    
    export const myDirective: Directive = {
        created(el: HTMLElement, binding: DirectiveBinding) {},
        beforeMount(el: HTMLElement, binding: DirectiveBinding) {},
        mounted(el: HTMLElement, binding: DirectiveBinding) {},
        beforeUpdate(el: HTMLElement, binding: DirectiveBinding) {},
        updated(el: HTMLElement, binding: DirectiveBinding) {},
        beforeUnmount(el: HTMLElement, binding: DirectiveBinding) {},
        unmounted(el: HTMLElement, binding: DirectiveBinding) {},
    };
    


  4. Your First Custom Directive: v-focus (Auto-Focus Input)

  5. src/
    └── directives/
        └── vFocus.ts
    
    import type { Directive } from "vue";
    
    export const vFocus: Directive = {
        mounted(el: HTMLElement) {
            el.focus();
        }
    };
    
    import { createApp } from "vue";
    import App from "./App.vue";
    import { vFocus } from "./directives/vFocus";
    
    const app = createApp(App);
    
    app.directive("focus", vFocus);
    
    app.mount("#app");
    
    <input v-focus />
    


  6. Example: v-color (Change Text Color Dynamically)
  7. export const vColor: Directive = {
        mounted(el: HTMLElement, binding) {
            el.style.color = String(binding.value);
        },
        updated(el: HTMLElement, binding) {
            el.style.color = String(binding.value);
        }
    };
    
    <p v-color="'red'">Red text</p>
    <p v-color="dynamicColor">Dynamic</p>
    


  8. Example: v-click-outside (Dropdown / Modal Usage)
  9. export const vClickOutside: Directive = {
        beforeMount(el: HTMLElement, binding) {
            el.__handler__ = (event: MouseEvent) => {
                if (!el.contains(event.target as Node)) {
                    binding.value(event);
                }
            };
            document.addEventListener("click", el.__handler__);
        },
        unmounted(el: HTMLElement) {
            document.removeEventListener("click", el.__handler__);
        }
    };
    
    <div v-click-outside="onOutside">
        Click outside me!
    </div>
    
    <script setup lang="ts">
    function onOutside() {
        alert("Clicked outside!");
    }
    </script>
    


  10. Example: v-autosize (Auto-Grow Textarea)
  11. export const vAutosize: Directive = {
        mounted(el: HTMLTextAreaElement) {
            const resize = () => {
                el.style.height = "auto";
                el.style.height = el.scrollHeight + "px";
            };
    
            el.__autosize__ = resize;
            el.addEventListener("input", resize);
            resize();
        },
        unmounted(el: HTMLTextAreaElement) {
            el.removeEventListener("input", el.__autosize__);
        }
    };
    
    <textarea v-autosize></textarea>
    


  12. Local Registration (Composition API)

  13. <script setup lang="ts">
    import { vColor } from "@/directives/vColor";
    
    const directives = { color: vColor };
    </script>
    
    <template>
        <p v-color="'blue'">Hello</p>
    </template>
    


  14. Passing Arguments and Modifiers
  15. <div v-example:foo.bar="123"></div>
    
    mounted(el, binding) {
        console.log(binding.value);      // 123
        console.log(binding.arg);        // "foo"
        console.log(binding.modifiers);  // { bar: true }
    }
    



Vue Plugins (Composition API + TypeScript)

  1. What Are Plugins in Vue?

  2. app.use(SomePlugin);
    


  3. Plugin Structure (Composition API + TypeScript)

  4. import type { App } from "vue";
    
    export default {
        install(app: App, options?: unknown) {
            // plugin logic
        }
    };
    


  5. Create a Simple Plugin (Global Utility Function)

  6. // LoggerPlugin.ts
    import type { App } from "vue";
    
    export default {
        install(app: App) {
            app.config.globalProperties.$log = (message: string) => {
                console.log("[LOG]", message);
            };
        }
    };
    
    import { createApp } from "vue";
    import App from "./App.vue";
    import LoggerPlugin from "./plugins/LoggerPlugin";
    
    const app = createApp(App);
    
    app.use(LoggerPlugin);
    
    app.mount("#app");
    
    <script setup lang="ts">
    const app = getCurrentInstance();
    app?.proxy?.$log("Hello plugin!");
    </script>
    


  7. Using TypeScript Types for Global Properties

  8. // src/types/vue.d.ts
    import "vue";
    
    declare module "vue" {
        interface ComponentCustomProperties {
            $log: (msg: string) => void;
        }
    }
    


  9. Plugin Accepting Options

  10. import type { App } from "vue";
    
    interface LoggerOptions {
        prefix?: string;
    }
    
    export default {
        install(app: App, options: LoggerOptions = {}) {
            const prefix = options.prefix ?? "LOG";
    
            app.config.globalProperties.$log = (msg: string) => {
                console.log(`[${prefix}]`, msg);
            };
        }
    };
    
    app.use(LoggerPlugin, { prefix: "MyApp" });
    


  11. Providing Global Values via provide/inject

  12. import type { App } from "vue";
    
    export const UserKey = Symbol("UserKey");
    
    export default {
        install(app: App) {
            const user = {
                name: "Junzhe",
                role: "admin"
            };
    
            app.provide(UserKey, user);
        }
    };
    
    <script setup lang="ts">
    import { inject } from "vue";
    import { UserKey } from "@/plugins/UserPlugin";
    
    const user = inject(UserKey);
    
    console.log(user?.name);
    </script>
    


  13. Plugin Adding a Global Component

  14. export default {
        install(app: App) {
            app.component("BaseButton", BaseButton);
        }
    };
    
    <BaseButton>Click</BaseButton>
    


  15. Plugin Adding a Global Directive
  16. export default {
        install(app: App) {
            app.directive("focus", {
                mounted(el: HTMLElement) {
                    el.focus();
                }
            });
        }
    };
    
    <input v-focus />
    


  17. A Realistic Example: Toast Notification Plugin
  18. import type { App } from "vue";
    
    interface ToastOptions {
        duration?: number;
    }
    
    export default {
        install(app: App, options: ToastOptions = {}) {
            const duration = options.duration ?? 2000;
    
            const toast = (msg: string) => {
                const div = document.createElement("div");
                div.textContent = msg;
                div.className = "toast";
                document.body.appendChild(div);
    
                setTimeout(() => {
                    document.body.removeChild(div);
                }, duration);
            };
    
            app.config.globalProperties.$toast = toast;
            app.provide("toast", toast);
        }
    };
    
    <script setup lang="ts">
    const app = getCurrentInstance();
    app?.proxy?.$toast("Hello!");
    </script>