0

A prop change with no effect on the component's DOM triggers its updated function, unexpectedly.

https://jsfiddle.net/e5gyuorL/1/

Same result for v-html="markup()" or {{markup()}} or computed: { markup: ... }.

Docs for updated (https://v2.vuejs.org/v2/api/#updated) say:

Called after a data change causes the virtual DOM to be re-rendered and patched.

How does one catch actual DOM re-renders? If this is a FAQ, apologies; I looked at length.

tony19
  • 125,647
  • 18
  • 229
  • 307
Liam
  • 495
  • 6
  • 21
  • What are you trying to achieve by knowing whether the DOM has changed? – Roy J Feb 17 '19 at 01:20
  • Embedding components dynamically based on DOM contents, see https://stackoverflow.com/questions/49139819/vue-components-in-user-defined-markdown – Liam Feb 17 '19 at 01:29
  • 1
    You can use a `MutationObserver` to listen for DOM changes. – connexo Feb 17 '19 at 10:18
  • Great suggestion! It's disappointing that there's no `mutated` hook in Vue :-( – Liam Feb 17 '19 at 19:43

1 Answers1

1

The most straightforward way I can think of is to have the component store its innerHTML in a data item, and on each update check to see whether it has changed:

Vue.component('t-markdown', {
  template: '#t-markdown',
  data: {
   innerHTML: ''
  },
  props: {src:String},
  methods: {
    markup: function() { return this.src.slice(0,11) },
  },
  updated: function() { 
   if (this.innerHTML !== this.$el.innerHTML) {
    this.$parent.count++;
      this.innerHTML = this.$el.innerHTML;
    }
   },
   mounted() {
    this.innerHTML = this.$el.innerHTML;
   }
});

new Vue({
  el: "#app",
  data: {count:0, inp:'<b>src</b> '},
  methods: {
    change: function() { this.inp += '#' },
  },
  mounted() {
   setTimeout(() => this.inp = '<i>changed!</i>', 7000);
  }
})
body {
  background: #20262E;
  padding: 20px;
  font-family: sans-serif, Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <t-markdown :src="inp"></t-markdown>
  <button @click="change">change</button> updated: {{count}}
  <div>
    {{inp}}
  </div>
</div>

<script type="text/x-template" id="t-markdown">
  <div v-html="markup()"></div>
</script>
Roy J
  • 42,522
  • 10
  • 78
  • 102
  • Thanks for the suggestion! I was hoping the answer wouldn't be "checksum innerHTML" (sadly, there's no checksum available in browsers). Storing a copy is even more problematic, tho. – Liam Feb 17 '19 at 19:41
  • What's the nature of the problem with it? – Roy J Feb 17 '19 at 21:16
  • The copy doubles the memory weight of every component instance. – Liam Feb 18 '19 at 04:23
  • No, a string is a _lot_ lighter than the DOM structure that is built from it. – Roy J Feb 18 '19 at 12:17