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.3 KiB
5.3 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | |||||
|---|---|---|---|---|---|---|---|---|---|
| Treat Directive Hook Arguments as Read-Only | MEDIUM | Modifying directive arguments causes unpredictable behavior and breaks Vue's internal state | gotcha |
|
Treat Directive Hook Arguments as Read-Only
Impact: MEDIUM - Apart from el, you should treat all directive hook arguments (binding, vnode, prevVnode) as read-only and never modify them. Modifying these objects can cause unpredictable behavior and interfere with Vue's internal workings.
If you need to share information across hooks, use the element's dataset attribute or a WeakMap.
Task Checklist
- Never mutate
binding,vnode, orprevVnodearguments - Use
el.datasetto share primitive data between hooks - Use a WeakMap for complex data that needs to persist across hooks
- Only modify
el(the DOM element) directly
Incorrect:
// WRONG: Mutating binding object
const vBadDirective = {
mounted(el, binding) {
// DON'T DO THIS - modifying binding
binding.value = 'modified' // WRONG!
binding.customData = 'stored' // WRONG!
binding.modifiers.custom = true // WRONG!
},
updated(el, binding) {
// These modifications may be lost or cause errors
console.log(binding.customData) // undefined or error
}
}
// WRONG: Mutating vnode
const vAnotherBadDirective = {
mounted(el, binding, vnode) {
// DON'T DO THIS
vnode.myData = 'stored' // WRONG!
vnode.props.modified = true // WRONG!
}
}
Correct:
// CORRECT: Use el.dataset for simple data
const vWithDataset = {
mounted(el, binding) {
// Store data on the element's dataset
el.dataset.originalValue = binding.value
el.dataset.mountedAt = Date.now().toString()
},
updated(el, binding) {
// Access previously stored data
console.log('Original:', el.dataset.originalValue)
console.log('Current:', binding.value)
console.log('Mounted at:', el.dataset.mountedAt)
},
unmounted(el) {
// Clean up dataset if needed
delete el.dataset.originalValue
delete el.dataset.mountedAt
}
}
// CORRECT: Use WeakMap for complex data
const directiveState = new WeakMap()
const vWithWeakMap = {
mounted(el, binding) {
// Store complex state
directiveState.set(el, {
originalValue: binding.value,
config: binding.arg,
mountedAt: Date.now(),
callbacks: [],
observers: []
})
},
updated(el, binding) {
const state = directiveState.get(el)
if (state) {
console.log('Original:', state.originalValue)
console.log('Current:', binding.value)
// Can safely modify state object
state.updateCount = (state.updateCount || 0) + 1
}
},
unmounted(el) {
// WeakMap auto-cleans when element is garbage collected
// but explicit cleanup is good for observers/listeners
const state = directiveState.get(el)
if (state) {
state.observers.forEach(obs => obs.disconnect())
directiveState.delete(el)
}
}
}
Using Element Properties
// CORRECT: Use element properties with underscore prefix convention
const vTooltip = {
mounted(el, binding) {
// Store on element with underscore prefix to avoid conflicts
el._tooltipInstance = createTooltip(el, binding.value)
el._tooltipConfig = { ...binding.modifiers }
},
updated(el, binding) {
// Access and update stored instance
if (el._tooltipInstance) {
el._tooltipInstance.update(binding.value)
}
},
unmounted(el) {
// Clean up
if (el._tooltipInstance) {
el._tooltipInstance.destroy()
delete el._tooltipInstance
delete el._tooltipConfig
}
}
}
What You CAN Modify
You are allowed to modify the el (DOM element) itself:
const vHighlight = {
mounted(el, binding) {
// CORRECT: Modifying el directly is allowed
el.style.backgroundColor = binding.value
el.classList.add('highlighted')
el.setAttribute('data-highlighted', 'true')
el.textContent = 'Modified content'
},
updated(el, binding) {
// CORRECT: Update el when binding changes
el.style.backgroundColor = binding.value
}
}
Binding Object Properties (Read-Only Reference)
The binding object contains:
value- Current value passed to directive (read-only)oldValue- Previous value (only in beforeUpdate/updated) (read-only)arg- Argument passed (e.g.,v-dir:arg) (read-only)modifiers- Object of modifiers (e.g.,v-dir.foo.bar) (read-only)instance- Component instance (read-only)dir- Directive definition object (read-only)
const vExample = {
mounted(el, binding) {
// READ these properties, don't modify them
console.log(binding.value) // Read: OK
console.log(binding.arg) // Read: OK
console.log(binding.modifiers) // Read: OK
console.log(binding.instance) // Read: OK
// Store what you need for later
el.dataset.directiveArg = binding.arg || ''
el.dataset.hasModifierFoo = binding.modifiers.foo ? 'true' : 'false'
}
}