Skip to the content.

General Notes for Vue 3

Creating a Vue Application

App Configurations

app.component("TodoDeleteButton", TodoDeleteButton);
// This makes the TodoDeleteButton available for use anywhere in our app.
// https://vuejs.org/guide/essentials/application.html#app-configurations

Template Syntax

Dynamically Binding Multiple Attributes

const objectOfAttrs = {
  id: 'container',
  class: 'wrapper'
}

<div v-bind="objectOfAttrs"></div>
// or simply
<div :objectOfAttrs></div>

Modifiers

<template>
  <form @submit.prevent="onSubmit">...</form>
</template>

Reactivity Fundamentals

Declaring Reactive State (Ref)

<script setup>
  import {ref} from 'vue' const count = ref(0) function increment(){" "}
  {count.value++}
</script>

<template>
  <button @click="increment">
    8
  </button>
</template>

Deep Reactivity

import { ref } from "vue";

const obj = ref({
  nested: { count: 0 },
  arr: ["foo", "bar"],
});

function mutateDeeply() {
  // these will work as expected.
  obj.value.nested.count++;
  obj.value.arr.push("baz");
}

Dom Update Timing

import { nextTick } from "vue";

async function increment() {
  count.value++;
  await nextTick();
  // Now the DOM is updated
}

Reactive

import { reactive } from 'vue'

const state = reactive({ count: 0 })

<template>
  <button @click="state.count++">
    
  </button>
</template>

Computed Properties

<script setup>
import { reactive, computed } from 'vue'

const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

// a computed ref
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script>

<template>
  <p>Has published books:</p>
  <span></span>
</template>

CLASS AND STYLE BINDINGS

Binding To Computed Property

const isActive = ref(true)
const error = ref(null)

const classObject = computed(() => ({
  active: isActive.value && !error.value,
  'text-danger': error.value && error.value.type === 'fatal'
}))

<template>
<div :class="classObject"></div>
</template>

Binding To Arrays

<template>
  <div :class="[{ active: isActive }, errorClass]"></div>
</template>

Binding With Components

<template>
  <!-- MyComponent template using $attrs -->
  <p :class="$attrs.class">Hi!</p>
  <span>This is a child component</span>
</template>
// parent component
<MyComponent class="baz" />

Multiple Values

<template>
  <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div>
</template>

Conditional Rendering

v-if vs. v-show

List Rendering

v-for

const parentMessage = ref('Parent')
const items = ref([{ message: 'Foo' }, { message: 'Bar' }])

<template>
<li v-for="(item, index) in items">
   -  - 
</li>
</template>

// Parent - 0 - Foo
// Parent - 1 - Bar
<template>
  <div v-for="item of items"></div>
</template>

v-for with v-if

<!--
This will throw an error because property "todo"
is not defined on instance.
-->
<li v-for="todo in todos" v-if="!todo.isComplete"></li>
<template v-for="todo in todos">
  <li v-if="!todo.isComplete"></li>
</template>

Maintaining State with key

<template v-for="todo in todos" :key="todo.name">
  <li></li>
</template>

v-for with a Component

<MyComponent v-for="item in items" :key="item.id" />
<MyComponent
  v-for="(item, index) in items"
  :item="item"
  :index="index"
  :key="item.id"
/>

Array Change Detection

Mutation Methods

- return numbers.reverse()
+ return [...numbers].reverse()

Event Handling

Inline Handlers

const count = ref(0)

<template>
  <button @click="count++">Add 1</button>
  <p>Count is: 8</p>
</template>

Method vs. Inline Detection

Event Modifiers

<!-- the click event's propagation will be stopped -->
<a @click.stop="doThis"></a>

<!-- the submit event will no longer reload the page -->
<form @submit.prevent="onSubmit"></form>

<!-- modifiers can be chained -->
<a @click.stop.prevent="doThat"></a>

<!-- just the modifier -->
<form @submit.prevent></form>

<!-- only trigger handler if event.target is the element itself -->
<!-- i.e. not from a child element -->
<div @click.self="doThat">...</div>

Key Modifiers

<!-- only call `submit` when the `key` is `Enter` -->
<input @keyup.enter="submit" />

Key Aliases

System Modifier Keys

Mouse Button Modifiers

​- These modifiers restrict the handler to events triggered by a specific mouse button.

Form Input Bindings

Basic Usage

Checkbox

<script setup>
import { ref } from 'vue'

const checkedNames = ref([])
</script>

<template>
  <div>Checked names: </div>

  <input type="checkbox" id="jack" value="Jack" v-model="checkedNames" />
  <label for="jack">Jack</label>

  <input type="checkbox" id="john" value="John" v-model="checkedNames" />
  <label for="john">John</label>

  <input type="checkbox" id="mike" value="Mike" v-model="checkedNames" />
  <label for="mike">Mike</label>
</template>

Radio

<script setup>
import { ref } from 'vue'

const picked = ref('One')
</script>

<template>
  <div>Picked: </div>

	<input type="radio" id="one" value="One" v-model="picked" />
	<label for="one">One</label>

	<input type="radio" id="two" value="Two" v-model="picked" />
  <label for="two">Two</label>
</template>

Select

Single Select

<script setup>
import { ref } from 'vue'

const selected = ref('')
</script>

<template>
  <span> Selected: </span>

  <select v-model="selected">
    <option disabled value="">Please select one</option>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
</template>

Multiple Select

<script setup>
import { ref } from 'vue'

const selected = ref([])
</script>

<template>
  <div>Selected: </div>

  <select v-model="selected" multiple>
    <option>A</option>
    <option>B</option>
    <option>C</option>
  </select>
</template>

<style>
select[multiple] {
  width: 100px;
}
</style>

Modifiers

.lazy

<!-- synced after "change" instead of "input" -->
<input v-model.lazy="msg" />

.number

<input v-model.number="age" />

.trim

<input v-model.trim="msg" />

Lifecycle Hooks

Watchers

<script setup>
import { ref, watch } from 'vue'

const question = ref('')
const answer = ref('Questions usually contain a question mark. ;-)')
const loading = ref(false)

// watch works directly on a ref
watch(question, async (newQuestion, oldQuestion) => {
  if (newQuestion.includes('?')) {
    loading.value = true
    answer.value = 'Thinking...'
    try {
      const res = await fetch('https://yesno.wtf/api')
      answer.value = (await res.json()).answer
    } catch (error) {
      answer.value = 'Error! Could not reach the API. ' + error
    } finally {
      loading.value = false
    }
  }
})
</script>

<template>
  <p>
    Ask a yes/no question:
    <input v-model="question" :disabled="loading" />
  </p>
  <p></p>
</template>

Deep Watchers

watch(
  () => state.someObject,
  (newValue, oldValue) => {
    // Note: `newValue` will be equal to `oldValue` here
    // *unless* state.someObject has been replaced
  },
  { deep: true }
);

Use with Caution: Deep watch requires traversing all nested properties in the watched object, and can be expensive when used on large data structures. Use it only when necessary and beware of the performance implications.

Eager Watchers

watch(
  source,
  (newValue, oldValue) => {
    // executed immediately, then again when `source` changes
  },
  { immediate: true }
);

watchEffect()

const todoId = ref(1);
const data = ref(null);

watch(
  todoId,
  async () => {
    const response = await fetch(
      `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
    );
    data.value = await response.json();
  },
  { immediate: true }
);
watchEffect(async () => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
  );
  data.value = await response.json();
});

watch vs. watchEffect

Stopping a Watcher

const unwatch = watchEffect(() => {});

// ...later, when no longer needed
unwatch();
// data to be loaded asynchronously
const data = ref(null);

watchEffect(() => {
  if (data.value) {
    // do something when data is loaded
  }
});