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>
4.7 KiB
4.7 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| KeepAlive Include/Exclude Requires Component Name | MEDIUM | The include and exclude props match against component name option, which must be explicitly declared | gotcha |
|
KeepAlive Include/Exclude Requires Component Name
Impact: MEDIUM - When using include or exclude props on KeepAlive, the matching is done against the component's name option. Components without an explicit name will not match and caching behavior will be unexpected.
Task Checklist
- Declare
nameoption on components used with include/exclude - Use
defineOptions({ name: '...' })in<script setup> - Note: Since Vue 3.2.34, SFC filename is auto-inferred as name
- Verify names match exactly (case-sensitive)
The Problem
<!-- App.vue -->
<template>
<KeepAlive include="TabA,TabB">
<component :is="currentTab" />
</KeepAlive>
</template>
<!-- TabA.vue - NO NAME DECLARED -->
<script setup>
// No name option - will NOT match "TabA" in include!
const count = ref(0)
</script>
<template>
<div>Count: {{ count }}</div>
</template>
Result: TabA is NOT cached because it has no name option to match against.
Solutions
Solution 1: Use defineOptions (Vue 3.3+)
<!-- TabA.vue -->
<script setup>
import { ref } from 'vue'
defineOptions({
name: 'TabA' // Now matches include="TabA"
})
const count = ref(0)
</script>
<template>
<div>Count: {{ count }}</div>
</template>
Solution 2: Dual Script Block
<!-- TabA.vue -->
<script>
export default {
name: 'TabA'
}
</script>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<div>Count: {{ count }}</div>
</template>
Solution 3: Rely on Auto-Inference (Vue 3.2.34+)
Since Vue 3.2.34, SFCs using <script setup> automatically infer their name from the filename:
TabA.vue -> name is "TabA"
UserProfile.vue -> name is "UserProfile"
my-component.vue -> name is "my-component" (kebab-case preserved)
<!-- Works automatically since Vue 3.2.34+ -->
<!-- File: TabA.vue -->
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<div>Count: {{ count }}</div>
</template>
<!-- App.vue -->
<template>
<!-- Matches filename "TabA" -->
<KeepAlive include="TabA">
<component :is="currentTab" />
</KeepAlive>
</template>
Common Mistakes
Mistake 1: Name Doesn't Match
<script>
export default {
name: 'tab-a' // lowercase kebab-case
}
</script>
<template>
<!-- DOESN'T MATCH - include uses PascalCase -->
<KeepAlive include="TabA">
<component :is="currentTab" />
</KeepAlive>
</template>
Fix: Ensure names match exactly:
<KeepAlive include="tab-a"> <!-- or change component name to 'TabA' -->
Mistake 2: Dynamic Components Without Names
<script setup>
import { defineAsyncComponent } from 'vue'
// Async component - might not have name!
const AsyncTab = defineAsyncComponent(() => import('./Tab.vue'))
</script>
Fix: Ensure the imported component has a name declared.
Mistake 3: Using Props in Options API
<script>
export default {
// This doesn't set the component name!
props: ['name'] // 'name' prop is different from component name option
}
</script>
Debugging Name Issues
Check what name Vue sees for your component:
<script setup>
import { getCurrentInstance, onMounted } from 'vue'
onMounted(() => {
const instance = getCurrentInstance()
console.log('Component name:', instance?.type?.name)
console.log('Component __name:', instance?.type?.__name)
})
</script>
Using Different Match Formats
<template>
<!-- String (comma-delimited) -->
<KeepAlive include="TabA,TabB,TabC">
<component :is="current" />
</KeepAlive>
<!-- RegExp -->
<KeepAlive :include="/^Tab/">
<component :is="current" />
</KeepAlive>
<!-- Array -->
<KeepAlive :include="['TabA', 'TabB']">
<component :is="current" />
</KeepAlive>
</template>
Key Points
- Name must match exactly - Case-sensitive string matching
- Vue 3.2.34+ auto-infers name - From filename for
<script setup>SFCs - Use
defineOptions- Clean way to set name in<script setup> - Debug with getCurrentInstance - Check what name Vue actually sees
- Multiple formats available - String, RegExp, or Array for include/exclude