Frontend Development 18 min read

Vue 3 Composition API: Setup, Reactive, Ref, Computed, Watch, Provide/Inject, Template Ref, and nextTick Tutorial

This article provides a comprehensive Vue 3 composition‑API tutorial covering project initialization, the setup function, reactive and ref utilities, computed properties, watch, provide/inject, template refs, and nextTick, with detailed code examples and explanations.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Vue 3 Composition API: Setup, Reactive, Ref, Computed, Watch, Provide/Inject, Template Ref, and nextTick Tutorial

1. Initialize Project

Install Vue CLI globally, create a new project, and add the composition‑API plugin.

// ① npm i -g @vue/cli
// ② vue create my-project
// ③ npm install @vue/composition-api -S

// ④ main.js
import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'
Vue.use(VueCompositionApi)

2. setup Function

The setup function is the new entry point for component logic in Vue 3, exposing all component properties and methods as a unified API.

2.1 Execution Timing

The setup function runs after beforeCreate and before created .

setup(props, ctx) {
    console.log('setup')
},
beforeCreate() {
    console.log('beforeCreate')
},
created() {
    console.log('created')
},

2.2 Receiving Props

// In setup, receive props
setup(props) {
    console.log(props)
},
props: {
    p1: String
}

2.3 Context Object

The second argument of setup is a context object that contains attributes, emit, listeners, parent, refs, root, etc., which were previously accessed via this in Vue 2.

setup(props, ctx) {
    console.log(ctx)
    console.log(this) // undefined
},
/* ctx contains:
attrs, emit, listeners, parent, refs, root, ... */

Note: this is not available inside setup .

3. reactive

The reactive function creates a reactive data object, equivalent to Vue.observable in Vue 2.

<template>
  <div>
    <p>Current count: {{count}}</p>
    <button @click="count += 1">+1</button>
  </div>
</template>

<script>
import { reactive } from '@vue/composition-api'
export default {
  setup() {
    const state = reactive({ count: 0 })
    state.count += 1
    console.log(state)
    return state
  }
}
</script>

4. ref

The ref function creates a reactive wrapper object that holds a single .value property.

<template>
  <div>
    <h3>02.ref.vue</h3>
    <p>refCount: {{refCount}}</p>
    <button @click="refCount += 1">+1</button>
  </div>
</template>

<script>
import { ref } from '@vue/composition-api'
export default {
  setup() {
    const refCount = ref(0)
    console.log(refCount.value) // 0
    refCount.value++
    console.log(refCount.value) // 1
    return { refCount }
  }
}
</script>

4.1 Accessing ref Inside a reactive Object

When a ref is placed inside a reactive object, it is automatically unwrapped, allowing direct access without .value .

setup() {
  const refCount = ref(0)
  const state = reactive({ refCount })
  console.log(state.refCount) // 0
  state.refCount++ // works without .value
  console.log(refCount) // 1
  return { refCount }
}

4.2 Ref Overwrites

Assigning a new ref to a property that already holds a ref replaces the old one.

setup() {
  const c1 = ref(0)
  const state = reactive({ c1 })
  const c2 = ref(9)
  state.c1 = c2
  state.c1++
  console.log(state.c1) // 10
  console.log(c2.value) // 10
  console.log(c1.value) // 0
}

5. isRef

isRef() checks whether a value is a ref object, useful when you need to unwrap a possibly ref value.

import { ref, reactive, isRef } from '@vue/composition-api'
export default {
  setup() {
    const unwrapped = isRef(foo) ? foo.value : foo
  }
}

6. toRefs

toRefs() converts a reactive object into a plain object whose properties are all ref s.

<template>
  <div>
    <h3>03.toRefs.vue</h3>
    <p>{{ count }} - {{ name }}</p>
    <button @click="count += 1">+1</button>
    <button @click="add">+1</button>
  </div>
</template>

<script>
import { reactive, toRefs } from '@vue/composition-api'
export default {
  setup() {
    const state = reactive({ count: 0, name: 'zs' })
    const add = () => { state.count += 1 }
    return { ...toRefs(state), add }
  }
}
</script>

7. computed

7.1 Read‑only Computed

<template>
  <div>
    <h3>04.computed.vue</h3>
    <p>refCount: {{refCount}}</p>
    <p>computedCount: {{computedCount}}</p>
    <button @click="refCount++">refCount + 1</button>
    <button @click="computedCount++">computedCount + 1</button>
  </div>
</template>

<script>
import { computed, ref } from '@vue/composition-api'
export default {
  setup() {
    const refCount = ref(1)
    const computedCount = computed(() => refCount.value + 1)
    return { refCount, computedCount }
  }
}
</script>

7.2 Read‑Write Computed

<script>
import { computed, ref } from '@vue/composition-api'
export default {
  setup() {
    const refCount = ref(1)
    const computedCount = computed({
      get: () => refCount.value + 1,
      set: val => { refCount.value = refCount.value - 5 }
    })
    console.log(computedCount.value)
    computedCount.value = 10
    console.log(computedCount.value)
    console.log(refCount.value)
    return { refCount, computedCount }
  }
}
</script>

8. watch

watch() monitors changes to reactive data and triggers callbacks.

8.1 Basic Usage

import { watch } from '@vue/composition-api'

8.2 Watching Reactive Sources

// Watching a reactive object
watch(() => state.count, (newVal, oldVal) => {
  console.log(newVal, oldVal)
}, { lazy: true })

8.3 Watching Multiple Sources

// Array of sources
watch([
  () => state.count,
  () => state.name
], ([newCount, newName], [oldCount, oldName]) => {
  console.log(newCount, oldCount)
  console.log(newName, oldName)
}, { lazy: true })

8.4 Stopping a Watcher

const stop = watch(() => { /* ... */ })
stop() // stops the watcher

8.5 Cleanup of Asynchronous Tasks

watch(keywords, (newVal, oldVal, onCleanup) => {
  const timerId = setTimeout(() => console.log(newVal), 1000)
  onCleanup(() => clearTimeout(timerId))
}, { lazy: true })

9. provide & inject

These functions enable data sharing between nested components, usable only inside setup .

9.1 Sharing Plain Data

// app.vue (parent)
provide('themecolor', color)

// son.vue (child)
const color = inject('themecolor')

9.2 Sharing ref Data

// app.vue
provide('themecolor', 'red')

// son.vue
const color = inject('themecolor')

10. Template Ref

10.1 DOM Ref

<template>
  <h3 ref="h3Ref">TemplateRefOne</h3>
</template>

<script>
import { ref, onMounted } from '@vue/composition-api'
export default {
  setup() {
    const h3Ref = ref(null)
    onMounted(() => { h3Ref.value.style.color = 'red' })
    return { h3Ref }
  }
}
</script>

10.2 Component Ref

// Parent component
const comRef = ref(null)
const showComRef = () => {
  console.log(comRef.value.str1)
  comRef.value.setStr1()
}

11. nextTick

<template>
  <button v-if="!isShowInput" @click="showInput">Show Input</button>
  <input v-else ref="ipt" type="text">
</template>

export default {
  data() { return { isShowInput: false } },
  methods: {
    showInput() {
      this.isShowInput = !this.isShowInput
      this.$nextTick(() => this.$refs.ipt.focus())
    }
  }
}

The article concludes with a brief promotional note linking to a site that hosts Google Cloud Next ’20 videos without a VPN.

VueReactiveComposition APIprovide/injectWatchrefSetupcomputed
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.