Subchapter 14.13
references/directives.mdMarkdown4 KBView on GitHub
Impact: MEDIUM - Directives are for low-level DOM access. Use them sparingly, keep them side-effect safe, and prefer components or composables when you need stateful or reusable UI behavior.
unmounted<script setup> with the v- prefixDirective bindings are not reactive storage. Don’t write to them.
const vFocus = {
mounted(el, binding) {
// binding.value is read-only
el.focus()
}
}Directives apply to DOM elements. When used on components, they attach to the root element and can break if the root changes.
BAD:
<MyInput v-focus />GOOD:
<!-- MyInput.vue -->
<script setup>
const vFocus = (el) => el.focus()
</script>
<template>
<input v-focus />
</template>Any timers, listeners, or observers must be removed to avoid leaks.
const vResize = {
mounted(el) {
const observer = new ResizeObserver(() => {})
observer.observe(el)
el._observer = observer
},
unmounted(el) {
el._observer?.disconnect()
}
}If you only need mounted/updated, use the function form.
const vAutofocus = (el) => el.focus()<script setup>
const vFocus = (el) => el.focus()
</script>
<template>
<input v-focus />
</template>Use Directive<Element, ValueType> so binding.value is typed, and augment Vue’s template types so directives are recognized in SFC templates.
BAD:
// Untyped directive value and no template type augmentation
export const vHighlight = {
mounted(el, binding) {
el.style.backgroundColor = binding.value
}
}GOOD:
import type { Directive } from 'vue'
type HighlightValue = string
export const vHighlight = {
mounted(el, binding) {
el.style.backgroundColor = binding.value
}
} satisfies Directive<HTMLElement, HighlightValue>
declare module 'vue' {
interface ComponentCustomProperties {
vHighlight: typeof vHighlight
}
}Directive hooks such as mounted and updated do not run during SSR. If a directive sets attributes/classes that affect rendered HTML, provide an SSR equivalent via getSSRProps to avoid hydration mismatches.
BAD:
const vTooltip = {
mounted(el, binding) {
el.setAttribute('data-tooltip', binding.value)
el.classList.add('has-tooltip')
}
}GOOD:
const vTooltip = {
mounted(el, binding) {
el.setAttribute('data-tooltip', binding.value)
el.classList.add('has-tooltip')
},
getSSRProps(binding) {
return {
'data-tooltip': binding.value,
class: 'has-tooltip'
}
}
}If a standard attribute or binding works, use it instead of a directive.
Use a directive for DOM-level behavior. Use a component when behavior affects structure, state, or rendering.