Inline Composables

You can define composables inline, keeping them in your SFC file:

<script setup>
const useCount = (i) => {
const count = ref(0);
const increment = () => count.value += 1;
const decrement = () => count.value -= 1;
return {
id: i,
count,
increment,
decrement,
};
};
const listOfCounters = [];
for (const i = 0; i < 10; i++) {
listOfCounters.push(useCount(i));
}
</script>
<template>
<div v-for="counter in listOfCounters" :key="counter.id">
<button @click="counter.decrement()">-</button>
{{ counter.count }}
<button @click="counter.increment()">+</button>
</div>
</template>

But is there any point to doing this?

If you’re keeping your components focused on a specific task (and you should be), then it stands to reason that the logic is also focused on a single task.

This means that if you wrap up all relevant logic into an inline composable, you’ve wrapped up all — or nearly all — the logic that this component has:

<script setup>
// Create an inline composable
const useStuff = () => {
<all_our_logic>
};
// ...only to destructure most of it to use in our template
const {
value,
anotherValue,
eventHandler,
anotherEventHandler
} = useStuff();
</script>

At which point, we might as well write our logic without that unnecessary wrapper:

<script setup>
const value = ...
const anotherValue = ...
const eventHandler = ...
const anotherEventHandler = ...
</script>

However, if you have do have logic that can be encapsulated nicely within this inline composable, it could make your code cleaner and easier to use.

Using lexical scoping to create more boundaries within your files helps you to understand and think through your code, which is always helpful.