YouTip LogoYouTip

Vue3 Composition Api

Vue3 Composition API

\n

The Vue3 Composition API is primarily used to improve the reusability of code logic in large components.

\n

Traditional components tend to grow in complexity and code volume as business requirements become more intricate, making the overall logic difficult to read and understand.

\n

In Vue3, the Composition API is utilized within the setup function.

\n

Within setup, we can group code by logical concerns, extract logic snippets, and share code across components. Therefore, the Composition API allows us to write more organized code.

\n

Image 1 Compare the following two code snippets:

\n

1. Traditional Component

\n

Image 2

\n

2. Composition API

\n

Image 3

\n
\n

setup Component

\n

The setup() function executes before the component's created() lifecycle hook.

\n

The setup() function accepts two parameters: props and context.

\n

The first parameter, props, is reactive and will be updated when new props are passed in.

\n

The second parameter, context, is a plain JavaScript object representing the component context, exposing other values that may be useful within setup.

\n
\n

Note: You should avoid using this in setup, as it will not resolve to the component instance. setup is called before data properties, computed properties, or methods are resolved, so they cannot be accessed within setup.

\n
\n

The following example uses the Composition API to define a counter:

\n

Example (src/App.vue)

\n
<template>\n\n<div>\n\n<p>Counter instance: {{ count }}</p>\n\n<input @click="myFn"type="button"value="Click me to add 1">\n\n</div>\n\n</template>\n\n<script>\n\n import {ref, onMounted} from 'vue';\n\nexport default {\n\n setup(){\n\n //To define a variable with an initial value of 0, use the ref method to assign it; directly assigning won't update the UI when the variable changes.\n\n let count = ref(0);\n\n// Define click event myFn\n\n function myFn(){\n\n console.log(count);\n\n count.value += 1;\n\n }\n\n// When the component is mounted, we use the onMounted hook to log some messages.\n\n onMounted(() => console.log('component mounted!'));\n\n// Variables or methods defined in the Composition API can be used externally in templates.\n\n return {count,myFn} // The returned function behaves the same as the method.\n\n }\n\n }\n\n</script>
\n

In Vue 3.0, we can use the new ref function to make any reactive variable work anywhere, as shown below:

\n
import { ref } from 'vue'let count = ref(0);
\n

The ref() function creates a reactive data object based on the given value. The return value is an object containing only a .value property.

\n

Inside the setup() function, reactive data created by ref() returns an object, so you must access it using .value.

\n

Example

\n
import{ ref } from 'vue'\n\nconst counter = ref(0)\n\nconsole.log(counter)// { value: 0 }\n\n console.log(counter.value)// 0\n\ncounter.value++\n\n console.log(counter.value)// 1
\n
\n

Vue Composition API Lifecycle Hooks

\n

In Vue 2, we implement lifecycle hooks as follows:

\n

Example

\n
export default{\n\n beforeMount(){\n\n console.log('V2 beforeMount!')\n\n},\n\n mounted(){\n\n console.log('V2 mounted!')\n\n}\n\n};
\n

In Vue 3's Composition API, lifecycle hooks can be implemented in the setup() function using functions prefixed with on:

\n

Example

\n
import{ onBeforeMount, onMounted } from 'vue';\n\nexport default{\n\n setup(){\n\n onBeforeMount(()=>{\n\n console.log('V3 beforeMount!');\n\n})\n\n onMounted(()=>{\n\n console.log('V3 mounted!');\n\n})\n\n}\n\n};
\n

The following table maps the Options API to the Composition API, showing how to call lifecycle hooks inside setup():

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
Vue2 Options-based APIVue Composition API
beforeCreatesetup()
createdsetup()
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyonBeforeUnmount
destroyedonUnmounted
errorCapturedonErrorCaptured
\n

Since setup runs around the beforeCreate and created lifecycle hooks, there is no need to explicitly define them. In other words, any code written in these hooks should be written directly inside the setup function.

\n

These functions accept a callback that will be executed when the hook is triggered by the component:

\n

Example

\n
setup(){\n\n ...\n\n// When the component is mounted, we use the onMounted hook to log some messages.\n\n onMounted(()=> console.log('component mounted!'));\n\n ...\n\n}
\n
\n

Template References

\n

When using the Composition API, the concepts of reactive references and template references are unified.

\n

To obtain a reference to an element or component instance within the template, we can declare a ref as usual and return it from setup():

\n

Example

\n
<template>\n\n<div ref="root">This is a root element</div>\n\n</template>\n\n<script>\n\nimport{ ref, onMounted } from 'vue'\n\nexport default{\n\n setup(){\n\nconst root = ref(null)\n\nonMounted(()=>{\n\n// DOM The element will be assigned to the ref after initial rendering.\n\n console.log(root.value)// <div>This is a root element</div>\n\n})\n\nreturn{\n\n root\n\n}\n\n}\n\n}\n\n</script>
\n

In the above example, we expose root in the render context and bind it to the div via ref="root" as its reference.

\n

refs used in templates behave like any other ref: they are reactive and can be passed into (or returned from) composables.

\n

Usage in v-for

\n

Template references in the Composition API do not receive special handling when used inside v-for. Instead, use a function reference to perform custom processing:

\n

Example

\n
<template>\n\n<div v-for="(item, i) in list":ref="el => { if (el) divs = el }">\n\n{{ item }}\n\n</div>\n\n</template>\n\n<script>\n\nimport{ ref, reactive, onBeforeUpdate } from 'vue'\n\nexport default{\n\n setup(){\n\nconst list = reactive([1,2,3])\n\nconst divs = ref([])\n\n// Ensure to reset the ref before each update.\n\n onBeforeUpdate(()=>{\n\n divs.value=[]\n\n})\n\nreturn{\n\n list,\n\n divs\n\n}\n\n}\n\n}\n\n</script>
\n

Watching Template References

\n

Watching changes to template references can replace the lifecycle hooks demonstrated in the previous examples.

\n

However, a key difference from lifecycle hooks is that watch() and watchEffect() run side effects before the DOM is mounted or updated. Therefore, when the listener runs, the template reference has not yet been updated.

\n

Example

\n
<template>\n\n<div ref="root">This is a root element</div>\n\n</template>\n\n<script>\n\nimport{ ref, watchEffect } from 'vue'\n\nexport default{\n\n setup(){\n\nconst root = ref(null)\n\nwatchEffect(()=>{\n\n// This side effect runs before DOM updates, therefore, the template ref does not yet hold a reference to the element.\n\n console.log(root.value)// => null\n\n})\n\nreturn{\n\n root\n\n}\n\n}\n\n}\n\n</script>
\n

Therefore, listeners using template references should be defined with the flush: 'post' option. This ensures side effects run after DOM updates, keeping template references synchronized with the DOM and pointing to the correct elements.

\n

Example

\n
<template>\n\n<div ref="root">This is a root element</div>\n\n</template>\n\n<script>\n\nimport{ ref, watchEffect } from 'vue'\n\nexport default{\n\n setup(){\n\nconst root = ref(null)\n\nwatchEffect(()=>{\n\n console.log(root.value)// =><div>This is a root element</div>\n\n},\n\n{\n\n flush:'post'\n\n})\n\nreturn{\n\n root\n\n}\n\n}\n\n}\n\n</script>
\n
\n

API Reference

\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
No.API & DescriptionExample
1setup() Entry point for the component's Composition API. Called during component instantiation to define reactive data and methods.export default { setup() { const count = ref(0); return { count }; } }
2ref() Creates a reactive reference to wrap primitive types or objects.const count = ref(0)
3computed() Creates a computed value derived from other reactive data.const doubledCount = computed(() => count.value * 2)
4reactive() Creates a reactive object, suitable for objects or arrays.const state = reactive({ count: 0 })
5readonly() Creates readonly reactive data, preventing modifications.const state = readonly({ count: 0 })
6watchEffect() Creates a reactive side effect that automatically tracks its reactive dependencies.watchEffect(() => { console.log(count.value) })
7watchPostEffect() Triggers a side effect after DOM updates.watchPostEffect(() => { console.log('Post effect') })
8watchSyncEffect() Synchronously executes a side effect when reactive dependencies change.watchSyncEffect(() => { console.log('Synchronized effect') })
9watch() Used to observe changes in reactive data; can specify particular reactive data or computed properties.watch(count, (newCount) => { console.log(newCount) })
10onWatcherCleanup() Cleans up watchers; called when the observed data is no longer needed.watch(count, () => {}, onWatcherCleanup(() => { console.log('cleaned up') }))
11isRef() Checks if a value is of type ref.isRef(count)
12unref() Unwraps the value of a ref.unref(count)
13toRef() Creates a reactive reference extracted from an object.const countRef = toRef(state, 'count')
14toValue() Unwraps a value of type ref or reactive.toValue(count)
15toRefs() Converts each property of a reactive object into an independent ref.const { count } = toRefs(state)
16isProxy() Checks if an object is a Vue proxy object.isProxy(state)
17isReactive() Checks if an object is a reactive object.isReactive(state)
18isReadonly() Checks if an object is a readonly reactive object.isReadonly(state)
19shallowRef() Creates a shallow ref, making only the first level of the object's properties reactive.const state = shallowRef({ count: 0 })
20triggerRef() Forces an update trigger for a ref.triggerRef(count)
21customRef() Creates a custom reactive reference, allowing developers to customize getters and setters.const count = customRef((track, trigger) => { let value = 0; return { get: () => value, set: (val) => { value = val; trigger() } } })
22shallowReactive() Creates a shallow reactive object where only the first level of properties is reactive.const state = shallowReactive({ count: 0 })
23shallowReadonly() Creates a shallow readonly reactive object where only the first level of properties is readonly.const state = shallowReadonly({ count: 0 })
24toRaw() Retrieves the raw object from a proxy object.const rawState = toRaw(state)
25markRaw() Marks an object as non-reactive, causing Vue to skip its reactive conversion.const obj = markRaw({ count: 0 })
26effectScope() Creates a new effect scope for managing side effects.const scope = effectScope()
27getCurrentScope() Gets the current effect scope.const scope = getCurrentScope()
28onScopeDispose() Executes a cleanup function when the effect scope is destroyed.onScopeDispose(() => { console.log('Scope disposed') })
29onMounted() Lifecycle hook called when the component is mounted.onMounted(() => { console.log('Component mounted') })
30onUpdated() Lifecycle hook called when the component is updated.onUpdated(() => { console.log('Component updated') })
31onUnmounted() Lifecycle hook called when the component is unmounted.onUnmounted(() => { console.log('Component unmounted') })
32onBeforeMount() Lifecycle hook called before the component is mounted.onBeforeMount(() => { console.log('Before mount') })
33onBe
← Julia FunctionsJulia Strings β†’