Pulled ALL skills from 15 source repositories: - anthropics/skills: 16 (docs, design, MCP, testing) - obra/superpowers: 14 (TDD, debugging, agents, planning) - coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth) - better-auth/skills: 5 (auth patterns) - vercel-labs/agent-skills: 5 (React, design, Vercel) - antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo) - Plus 13 individual skills from various repos Mosaic Stack is not limited to coding — the Orchestrator and subagents serve coding, business, design, marketing, writing, logistics, analysis, and more. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.2 KiB
5.2 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Use Composables Instead of Mixins for Logic Reuse | HIGH | Mixins cause naming conflicts, unclear data origins, and inflexible logic - composables solve all these problems | best-practice |
|
Use Composables Instead of Mixins for Logic Reuse
Impact: HIGH - Mixins, the primary logic reuse mechanism in Options API, have fundamental flaws that make code hard to maintain. Composables (Composition API functions) solve all mixin drawbacks: unclear property origins, naming conflicts, and inability to parameterize.
The ability to create clean, reusable logic through composables is the primary advantage of the Composition API.
Task Checklist
- Migrate existing mixins to composables when refactoring
- Never create new mixins - use composables instead
- Use explicit imports to make data origins clear
- Parameterize composables to make them flexible
- Prefix composables with "use" (useAuth, useFetch, useForm)
Problems with Mixins:
// userMixin.js
export const userMixin = {
data() {
return {
user: null,
loading: false // Conflict waiting to happen!
}
},
methods: {
fetchUser() { /* ... */ }
}
}
// authMixin.js
export const authMixin = {
data() {
return {
token: null,
loading: false // NAME CONFLICT with userMixin!
}
},
methods: {
login() { /* ... */ }
}
}
// Component using mixins - PROBLEMATIC
export default {
mixins: [userMixin, authMixin],
mounted() {
// PROBLEM 1: Where does 'user' come from? Have to check mixins
console.log(this.user)
// PROBLEM 2: Which 'loading'? Last mixin wins, silently!
console.log(this.loading) // Is this user loading or auth loading?
// PROBLEM 3: Can't customize behavior per-component
this.fetchUser() // Always fetches the same way
}
}
Composables Solution:
// composables/useUser.js
import { ref } from 'vue'
export function useUser(userId) { // Can accept parameters!
const user = ref(null)
const loading = ref(false)
const error = ref(null)
async function fetchUser() {
loading.value = true
try {
user.value = await api.getUser(userId)
} catch (e) {
error.value = e
} finally {
loading.value = false
}
}
return { user, loading, error, fetchUser }
}
// composables/useAuth.js
import { ref } from 'vue'
export function useAuth() {
const token = ref(null)
const loading = ref(false) // No conflict - it's scoped!
async function login(credentials) { /* ... */ }
function logout() { /* ... */ }
return { token, loading, login, logout }
}
// Component using composables - CLEAR AND FLEXIBLE
<script setup>
import { useUser } from '@/composables/useUser'
import { useAuth } from '@/composables/useAuth'
// SOLUTION 1: Clear where everything comes from
const { user, loading: userLoading, fetchUser } = useUser(123)
const { token, loading: authLoading, login } = useAuth()
// SOLUTION 2: Rename to avoid any conflicts
// userLoading vs authLoading - explicit!
// SOLUTION 3: Parameterize behavior
const adminUser = useUser(adminId)
const currentUser = useUser(currentUserId)
// Each has its own state!
onMounted(() => {
fetchUser() // Explicitly from useUser
})
</script>
Migrating from Mixins
// BEFORE: Mixin with options
export const formMixin = {
data() {
return { errors: {}, submitting: false }
},
methods: {
validate() { /* ... */ },
submit() { /* ... */ }
}
}
// AFTER: Composable with flexibility
export function useForm(initialValues, validationSchema) {
const values = ref({ ...initialValues })
const errors = ref({})
const submitting = ref(false)
const touched = ref({})
function validate() {
errors.value = validationSchema.validate(values.value)
return Object.keys(errors.value).length === 0
}
async function submit(onSubmit) {
if (!validate()) return
submitting.value = true
try {
await onSubmit(values.value)
} finally {
submitting.value = false
}
}
function reset() {
values.value = { ...initialValues }
errors.value = {}
touched.value = {}
}
return {
values,
errors,
submitting,
touched,
validate,
submit,
reset
}
}
// Usage - now parameterizable and explicit
const loginForm = useForm(
{ email: '', password: '' },
loginValidationSchema
)
const registerForm = useForm(
{ email: '', password: '', name: '' },
registerValidationSchema
)
Composition Over Mixins Benefits
| Aspect | Mixins | Composables |
|---|---|---|
| Property origin | Unclear | Explicit import |
| Naming conflicts | Silent overwrites | Explicit rename |
| Parameters | Not possible | Fully supported |
| Type inference | Poor | Excellent |
| Reuse instances | One per component | Multiple allowed |
| Tree-shaking | Not possible | Fully supported |