The useMutation() composable watches for changes being made to the DOM tree of an element (or of a component): child nodes added or removed, attributes changed, text changed. Under the hood it uses the Mutation Observer API.
It is the setup-code counterpart of the v-mutation directive. Use the composable when you want to observe your component’s own root, or any element or component ref, without touching the template.
On the server-side of SSR or SSG modes, the composable never observes anything.
The composable can also be called outside of setup(): in a boot file, a store or a plain module. There is no component root to fall back on and no mount to wait for, so supply a target (an element, or a ref or getter of one); the observation starts right away and nothing stops it by itself: call stopMutation() when you are done.
Syntax
import { useTemplateRef } from 'vue'
import { useMutation } from 'quasar'
setup () {
const target = useTemplateRef('target') // an Element or a component
const { mutationRecords, stopMutation } = useMutation({
// all optional:
target, // (default: the component's own root element)
// MutationObserver options (default: none set, which observes
// every kind of change)
childList: true,
attributes: true,
characterData: true,
subtree: true,
attributeOldValue: true,
characterDataOldValue: true,
attributeFilter: [ 'class', 'style' ],
once: true, // stop after the first batch of records (default: false)
disabled: true, // pause observing (default: false)
onMutation (records) { // called with the Array of MutationRecord
// return false to stop observing for good
}
})
// ...
}function useMutation(
options?: MaybeRefOrGetter<{
target?: MaybeRefOrGetter<
Element | ComponentPublicInstance | null | undefined
>
childList?: boolean
attributes?: boolean
characterData?: boolean
subtree?: boolean
attributeOldValue?: boolean
characterDataOldValue?: boolean
attributeFilter?: string[]
once?: boolean
disabled?: boolean
onMutation?: (records: MutationRecord[]) => false | void
}>
): {
mutationRecords: ShallowRef<MutationRecord[]>
stopMutation: () => void
}Without a target, the composable observes the root element of the component it is called in, as of the moment the component gets mounted. A component rendering a fragment (multiple root nodes) has no root element to observe, so supply a target there.
Reading the Mutation Observer API first will be best in your understanding of the observing options. When none of childList, attributes, characterData, subtree, attributeOldValue, characterDataOldValue or attributeFilter is set, every kind of change gets observed, with the old values included, the same as the v-mutation directive without modifiers. Setting any of them observes only what you ask for; at least one of childList, attributes or characterData must then be set (or attributeFilter, which implies attributes), as subtree or the old-value flags alone make the native observer throw.
Every batch of MutationRecord that the browser delivers after the changes lands in the reactive mutationRecords (the last batch only, so it never grows) and is handed to the onMutation handler. Returning false from the handler stops the observation for good. With once, the observation stops by itself after the first batch.
stopMutation() ends the observation for good. You will rarely need it, as the composable stops by itself when the component gets destroyed.
Whatever your handler (or the reactive state it updates) does to the observed DOM is a new mutation, which calls the handler again, and so on without end: rendering the state inside the observed element, toggling a class on it, appending a node to it. Keep the effects of the handler out of the observed element, or observe only what you need (the element’s own attributes, its direct childList).
Changing the options while running
The options can be a plain Object, a Ref or a getter Function. A plain Object is read once. With a Ref or a getter, the composable tracks whatever reactive state the options read and re-applies them whenever that state changes, so you never call anything to “update” it:
- toggling
disabledpauses and resumes the observation (aoncethat already fired stays off for as long asonceholds; settingonceback tofalsestarts observing again) - pointing
targetto another element (or letting a template ref change throughv-if) follows it - changing what gets observed applies right away, keeping the changes not delivered yet
- swapping
onMutationtakes effect from the next batch
import { ref } from 'vue'
import { useMutation } from 'quasar'
setup () {
const paused = ref(false)
useMutation(() => ({
childList: true,
disabled: paused.value,
onMutation (records) { /* ... */ }
}))
// ...
}Example
Observing a list through a template ref: add, remove and rename items, and see every batch of changes as the browser reports it through mutationRecords: