In Vue 2, components often encounter significant architectural constraints as they grow in complexity. These limitations primarily manifest in two areas: managing code readability and facilitating logic reuse across components.
Challenges in Vue 2's Options API
The Options API organizes code by configuration blocks such as data, methods, computed, and watch. While straightforward for small components, this structure scatters code for a single feature across multiple sectoins, hindering comprehension. For example, consider this component that manages data from a person object:
const PersonComponent = {
data() {
return {
person: { fullName: 'Alex', years: 25 },
displayName: '',
displayAge: ''
}
},
methods: {
fetchName() {
this.displayName = this.person.fullName;
},
fetchAge() {
this.displayAge = this.person.years;
}
}
};
const appInstance = Vue.createApp(PersonComponent);
appInstance.mount('#root');
In this example, the logic for managing a person's name is split: the person.fullName is defined in data, and the fetchName method is in methods. For a larger component involving props, computed, and watch, the related pieces of a single feature become even more dispersed, creating a graph of fragmented code that is difficult to trace and maintain.
The Composition API's Approach to Code Organization
Vue 3's Composition API, centered on the setup() function, allows developers to collocate reactive data and their associated logic. The same functionality is reimplemented as follows:
const PersonComponent = {
setup() {
const person = { fullName: 'Alex', years: 25 };
// Logic for name
const displayName = Vue.ref('');
const fetchName = () => {
displayName.value = person.fullName;
};
// Logic for age
const displayAge = Vue.ref('');
const fetchAge = () => {
displayAge.value = person.years;
};
return { displayName, fetchName, displayAge, fetchAge };
}
};
Here, the reactive state (displayName, displayAge) and the functions that operate on them (fetchName, fetchAge) are grouped by feature within the setup() function. This co-location creates a clear, self-contained block for each concern, significantly improving code navigability.
To prevent the setup() function from becoming monolithic, related logic can be extracted into stendalone composition functions, which are then integrated.
// Composition function for name-related logic
function usePersonName(personData) {
const name = Vue.ref('');
const updateName = () => {
name.value = personData.fullName;
};
return { name, updateName };
}
// Composition function for age-related logic
function usePersonAge(personData) {
const age = Vue.ref('');
const updateAge = () => {
age.value = personData.years;
};
return { age, updateAge };
}
const PersonComponent = {
setup() {
const person = { fullName: 'Alex', years: 25 };
return {
...usePersonName(person),
...usePersonAge(person)
};
}
};
This pattern allows the setup() function to act as a composition root, cleanly aggregating distinct feature sets.
Enabling Logic Reuse Across Components
The extracted composition functions are inherently reusable. A different component can import and use the same usePersonName and usePersonAge functions with its own data, demonstrating the Composition API's power for logic sharing.
// A separate component reusing the logic
const AnotherPersonComponent = {
setup() {
const differentPerson = { fullName: 'Taylor', years: 30 };
return {
...usePersonName(differentPerson),
...usePersonAge(differentPerson)
};
},
template: `
<div>
<p>{{ name }}</p>
<button @click="updateName">Get Name</button>
</div>
<div>
<p>{{ age }}</p>
<button @click="updateAge">Get Age</button>
</div>
`
};
By grouping related code and encapsulating logic into independent functions, the Composition API directly tackles the core issues of poor code organization and difficult logic reuse inherent in the Options API paradigm.