Inconsistent custom directive behaviour with element innerHTML
Vue version
3.2.45
Link to minimal reproduction
Steps to reproduce
- Create a ref with a value that changes over time (using setInterval or something similar).
- Render the ref in a span using curly braces.
- Render the ref in another span using
v-text
. - Create a custom directive that updates an element's DOM using innerHTML. It uses the
updated
hook. - Use the custom directive on previously created spans.
What is expected?
Consistent rendering behaviour between both spans.
What is actually happening?
The span using curly braces looks like it performs DOM manipulation on the old DOM. This behaviour seems like it should occur when using beforeUpdate
hook instead of updated
.
The span using v-text
performs DOM manipulation on the latest version of the element. This feels correct based on the fact it is called on the updated
hook.
System Info
No response
Any additional comments?
I found a related issue (https://github.com/vuejs/core/issues/7207) - the comments on it that argue against manipulating the DOM seem to run counter to what's outlined in the docs (https://vuejs.org/guide/reusability/custom-directives.html#directive-hooks). I'd like to understand whether DOM manipulation is or isn't recommended when using custom directives.
You can manipulate the DOM as long as you don't mess with the parts Vue controls.
In your example, the "mustache" part is a DOM text node inside of a span. That text node is controlled by Vue, its content is being updated by Vue with the mustache code's value on each re-render.
But after you did replace this text node for the first time with your directive's use of innerHTML, Vue will continue to update the old text node that is no longer in the document because you replaced it, because Vue is not aware of the change you made.
I'd like to understand whether DOM manipulation is or isn't recommended when using custom directives.
Things you can touch:
- add content to empty elements.
- mess with element content that you previously added to empty elements.
- add/change/remove attributes/DOM properties that you did not also use in the template
- call a method on a DOM element, i.e. element.focus() on an input
- add/remove event listeners, trigger custom events, etc.
Things you should not touch:
- anything that Vue also touches on that element in your template.