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.
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 watcher8.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.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.