Vue 3 Composition API Standards · VUE-02
The Composition API in Vue is a powerful and flexible way to write components, offering an alternative to the Options API used in Vue 2.
It provides a more granular and function-based approach to managing component logic.
Use ref over reactive for State Management · VUE-02.01 · MUST
ref over reactive for State Management · VUE-02.01 · MUSTProviding explicit reactivity control through ref improves performance with more granular reactivity. With Typescript, these provide strong type inference.
The same reactivity is exposed via reactive, but it has many limitations that ref does not. Furthermore, reactive causes maintainability issues.
Links supporting why we use ref over reactive:
- Vue JS Docs Limitations of Reactive
- Please stop using reactive() in Vue 3 Composition API
- Should I Use Ref or Reactive in Vue 3?
Key Points 🔑
- Vue.js recommend using
refas the primary API for declaring reactive state. In 99% of cases, you will want to usereffor reactivity. reactiveonly takes objects, and not JS primitives (string,boolean,number, etc.)refworks on any type, primitive or complex.refis callingreactivebehind the scenes, so they both work with objects.refhas a.valueproperty for reassignging,reactivedoes not and therefore cannot be reassigned.- Due to the above reasons, in most cases you should use
reffor reactivity.
Use ref for Reactive State · VUE-02.01.01 · MUST
Use ref when:
- Creating a reactive state of a primitive.
- Creating a reactive state for an object you need to later reassign (such as an
array). - Directly binding values to the component template.
ref Example
<script setup>
import { ref } from 'vue';
// Use ref for a single reactive variable
const count = ref(0);
function increment() {
count.value++;
}
</script>
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>Only use reactive for Reactive State of Immutable Objects · VUE-02.01.02 · MUST
Use reactive when:
- Creating a reactive state of a complex
objectorarray. - Working with an object you don’t need to reassign.
reactive Example
<script setup>
import { reactive } from 'vue';
// Use reactive for an object with multiple properties
const user = reactive({
firstName: 'John',
lastName: 'Doe',
age: 30
});
function updateAge() {
user.age++;
}
</script>
<template>
<div>
<p>{{ user.firstName }} {{ user.lastName }} - Age: {{ user.age }}</p>
<button @click="updateAge">Increase Age</button>
</div>
</template>Further limitations of reactive
Using reactive has some limitations due to being an unwrapped JS Proxy.
The value type you assign to reactive cannot be reassigned, only the properties may be manipulated.
It also does not support destructuring, and any destructured properties will lose reactivity. This can lead to unmaintainable code.
Use Lifecycle Hooks to manage behaviour at different component stages · VUE-02.02 · MUST
Use onMounted() to Encapsulate Logic that Needs to Run After the Component is Added to the DOM · VUE-02.02.01 · MUST
Ensure consistent and efficient use of the onBeforeMount, onMounted, onUpdated, onErrorCaptured lifecycle hook in Vue components for executing code after the component is mounted.
onMounted() Example (✅)
<script setup>
import { ref, onMounted } from 'vue';
const message = ref('');
onMounted(() => {
message.value = 'Component is mounted!';
initializeComponent();
});
function initializeComponent() {
// Additional initialization logic
}
</script>Use onBeforeMount() to Encapsulate Logic that Needs to Run Before the Component is Added to the DOM · VUE-02.02.01 · MUST
<script setup>
import { onBeforeMount, onUpdated } from 'vue';
onBeforeMount(() => {
console.log('Component is about to be mounted.');
});
</script>Use onBeforeUpdate() & onUpdated() to Encapsulate Logic that Needs to Run When the Component Updated on the DOM · VUE-02.02.02 · MUST
Reactive State Example
<script setup>
import { onBeforeMount, onUpdated } from 'vue';
const count = ref(0)
const val = ref(0)
onBeforeUpdate(() => {
count.value++
console.log('beforeUpdate')
})
onUpdated(() => {
console.log('updated() val: ' + val.value)
})
</script>Use onBeforeUnmount() & onUnmounted() to Encapsulate Logic that Needs to Run When the Component is Being Destroyed · VUE-02.02.03 · MUST
Cleanup Example
<script setup>
import { onBeforeUnmount, onUnmounted } from 'vue';
onBeforeUnmount(() => {
console.log('Component is about to be unmounted.');
});
onUnmounted(() => {
console.log('Component has been unmounted.');
});
</script>Use onErrorCaptured() to Encapsulate Logic that Runs for Errors In Child Components · VUE-02.02.03 · MUST
Error Handling Example
<script setup>
import { onErrorCaptured } from 'vue';
onErrorCaptured((error, instance, info) => {
console.error('Error captured:', error, info);
return false; // Prevent further propagation
});
</script>Use Watchers watchEffect & watch to Encapsulate Logic that Runs on Reactive Effects · VUE-02.02.03 · MUST
Watchers Example
<script setup>
import { ref, watch, watchEffect } from 'vue';
const count = ref(0);
watch(count, (newVal) => {
console.log('Count changed:', newVal);
});
watchEffect(() => {
console.log('Count is:', count.value);
});
</script>Use Slots for Flexible Component Composition · VUE-02.3 · MUST
Slots provide a way to compose components and pass content into them. They allow for flexible and reusable component design by letting parent components pass content into child components.
Slots in Vue are a way to pass content from a parent component into a child component. They allow you to define placeholders in a component’s template where dynamic content can be inserted.
This makes components highly flexible and reusable, as they enable injection of customizable content based on their usage context.
Use Default Slots for Singular Data Transmission · VUE-02.3.1 · MUST
Use default slots to pass content into a component when no specific slot name is provided.
Default Slots Example (✅)
Default Slots ParentComponent.vue
<template>
<ChildComponent>
<p>This is default slot content.</p>
</ChildComponent>
</template>Default Slots ChildComponent.vue
<template>
<div>
<slot></slot> <!-- Placeholder for default slot content -->
</div>
</template>Use Named Slots when Placing Multiple Outlets in a Single Component · VUE-02.3.2 · MUST
Named Slots allow for more control over where the content is placed within the child component’s template.
Named Slots Example (✅)
Named Slots ParentComponent.vue
<template>
<ChildComponent>
<template #header>
<h1>Header Content</h1>
</template>
<template #footer>
<p>Footer Content</p>
</template>
</ChildComponent>
</template>Named Slots ChildComponent.vue
<template>
<div>
<header>
<slot name='header'></slot>
</header>
<main>
<p>Main Content</p>
</main>
<footer>
<slot name='footer'></slot>
</footer>
</div>
</template>Use Scoped Slots to Share Reusable Logic or Structure Across Different Components with Varying Data or Content · VUE-02.3.3 · MUST
Scoped Slots allows a parent component to access the state in a child component, enabling more flexible and reusable component design.
Scoped Slots Example (✅)
Scoped Slots ParentComponent.vue
<template>
<ItemList :items="items">
<template #default="{ item }">
<div>{{ item.name }} - {{ item.description }}</div>
</template>
</ItemList>
</template>
<script setup>
import { ref } from 'vue';
import ItemList from './ItemList.vue';
const items = ref([
{ name: 'Item 1', description: 'Description 1' },
{ name: 'Item 2', description: 'Description 2' }
]);
</script>Slots ChildComponent.vue
<template>
<div>
<div v-for="item in items" :key="item.name">
<slot :item="item"></slot>
</div>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
items: {
type: Array,
required: true
}
});
</script>Use setup Function for Component Initialization · VUE-02.4 · MUST
setup Function for Component Initialization · VUE-02.4 · MUSTThe setup() hook centralises all component initialisation and initialises reactive state. Making it easier to understand a component’s dependencies, and the logic it encapsulates.
Setup Example
<template>
<div>{{ message }}</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const message = ref('Hello, Composition API!');
return {
message,
};
},
};
</script>