The Composition API in Vue 3 introduces the concept of Hook functions, offering a more modular and reusable approach to state management and logic organization.
Custom Hooks
First, we create a custom Hook, such as useCounter
, to encapsulate counter logic:
// useCounter.js
import { ref } from 'vue';
export function useCounter() {
const count = ref(0);
function increment() {
count.value++;
}
function decrement() {
count.value--;
}
return {
count,
increment,
decrement
};
}
In the code above, ref
creates reactive data, and the increment
and decrement
methods increase or decrease the counter’s value, respectively. The return
statement exposes the data and methods for direct use in components.
Then, in a component, we can import and use this Hook:
<template>
<div>
<h1>Count: {{ count }}</h1>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script>
import { defineComponent } from 'vue';
import { useCounter } from './useCounter';
export default defineComponent({
setup() {
const { count, increment, decrement } = useCounter();
return {
count,
increment,
decrement
};
}
});
</script>
In the component’s setup
function, we call the useCounter
Hook and destructure its returned object into local variables. These variables are reactive and can be used directly in the template.
Benefits of this pattern:
- Logic Separation: Hooks encapsulate specific business logic, making component code clearer and easier to maintain.
- Reusability: Hooks can be shared across multiple components, reducing code duplication.
- Type Safety: In TypeScript, custom Hooks can include type definitions, improving code readability and predictability.
-
Reactive System: Reactive data created with
ref
orreactive
ensures automatic view updates when the state changes.
The Composition API combined with the Hooks pattern enables developers to organize and reuse code more flexibly, enhancing maintainability and development efficiency.
Built-in Hooks
In addition to custom Hooks, Vue 3’s Composition API provides several built-in Hooks, such as watch
, onMounted
, onBeforeUpdate
, onUpdated
, onBeforeUnmount
, onUnmounted
, toRefs
, computed
, and watchEffect
.
1. onMounted
: Executes after the component is mounted
import { onMounted } from 'vue';
export default {
setup() {
let element;
onMounted(() => {
element = document.querySelector('#someElement');
// Perform DOM-dependent initialization here
});
return {};
}
};
2. computed
: Computed properties, similar to Vue 2’s computed properties
import { ref, computed } from 'vue';
export default {
setup() {
const base = ref(10);
const multiplier = ref(2);
const result = computed(() => {
return base.value * multiplier.value;
});
return {
base,
multiplier,
result
};
}
};
3. watch
: Watches for changes in reactive data
import { ref, watch } from 'vue';
export default {
setup() {
const base = ref(10);
const multiplier = ref(2);
watch(base, (newBase, oldBase) => {
console.log(`Base changed from ${oldBase} to ${newBase}`);
});
return {
base,
multiplier
};
}
};
4. watchEffect
: Runs a side-effect function whenever any dependency changes
import { ref, watchEffect } from 'vue';
export default {
setup() {
const base = ref(10);
const multiplier = ref(2);
watchEffect(() => {
console.log(`Result: ${base.value * multiplier.value}`);
});
return {
base,
multiplier
};
}
};
5. toRefs
: Converts object properties to reactive references for direct use in templates
import { ref, toRefs } from 'vue';
export default {
props: {
user: {
type: Object,
required: true
}
},
setup(props) {
const { name, age } = toRefs(props.user);
return {
name,
age
};
}
};
6. reactive
: Creates a reactive object, enabling deep monitoring of property changes
import { reactive } from 'vue';
export default {
setup() {
const state = reactive({
count: 0,
user: {
name: 'John Doe',
age: 30
}
});
function increment() {
state.count++;
}
return {
state,
increment
};
}
};
7. provide/inject
: Provides and injects dependencies, similar to Vue 2’s non-reactive version, but supports reactive data in Vue 3
// Parent component
import { provide } from 'vue';
export default {
setup() {
const theme = ref('light');
provide('theme', theme);
return {};
}
};
// Child component
import { inject } from 'vue';
export default {
setup() {
const theme = inject('theme');
return {
theme
};
}
};
8. watch
with Options: Provides fine-grained control, such as deep watching, async handling, and triggering on initial value changes
import { ref, watch } from 'vue';
export default {
setup() {
const base = ref(10);
const multiplier = ref(2);
watch(
[base, multiplier],
([newBase, newMultiplier], [oldBase, oldMultiplier]) => {
if (newBase !== oldBase || newMultiplier !== oldMultiplier) {
console.log(`Result: ${newBase * newMultiplier}`);
}
},
{
deep: true, // Deep watching
immediate: true // Trigger callback immediately on initial value change
}
);
return {
base,
multiplier
};
}
};
9. effectScope
: Runs side effects within a specific scope for easier cleanup and management
import { effectScope, ref } from 'vue';
export default {
setup() {
const scope = effectScope();
const count = ref(0);
const stopEffect = scope.run(() => {
console.log('Effect running');
scope.watch(count, () => {
console.log(`Count is now: ${count.value}`);
});
});
return {
count,
stopEffect
};
}
};
Top comments (0)