Skip to content
Advertisement

Vue.js Note that mounted does not guarantee that all child components have also been mounted?

Can someone please explain notice below from VUE doc https://v2.vuejs.org/v2/api/#mounted ?

Note that mounted does not guarantee that all child components have also been mounted.

I have tested mounting children and subchildren but both are always mounted before root/app component, so it is safe to use “mounted” without $nextTick to access DOM. Children and subchildren components can access DOM element from root/app template although it is mounted after children. On the other way root/app can also access DOM elements in children templates. Consider this example…

<div id="app">
  {{ message }}
  <child></child>
  <span id="root"></span>
</div>

<script type="text/javascript">
  var subchild = {
    template: `<div>SUBCHILD<span id="subchild"></span></div>`,
    mounted() {
      console.log("Mounted SUBCHILD", document.getElementById('root'));
    }
  };
  
  var child = {
    template: `<div>CHILD<span id="child"></span><subchild></subchild></div>`,
    components : { subchild },
    mounted() {
      console.log("Mounted CHILD", document.getElementById('root'));
    }
  };
  
  var app = new Vue({
    el: '#app',
    components : { child },
    data() {
      return { message: 'APP' };
    },
    mounted() {
      console.log("Mounted APP", document.getElementById('root'), document.getElementById('child'), document.getElementById('subchild'));
    }
  
  })
</script>

https://jsfiddle.net/ecobyxrn/

Output in console…

Mounted SUBCHILD [object HTMLSpan…] [object HTMLSpan…] [object HTMLSpan…]

Mounted CHILD [object HTMLSpan…] [object HTMLSpan…] [object HTMLSpan…]

Mounted APP [object HTMLSpan…] [object HTMLSpan…] [object HTMLSpan…]

So in my case I do not understand that VUE notice. Can someone show use case that children are mounted later then root/app? I think VUE doc is very poor and does not explain it very well. I also tried delayed mount of root/app by commenting out el: '#app' + adding setTimeout(function() { app.$mount('#app'); }, 2000); but result is the same.

In addition see this image… VUE.js mounted hooks parent + child

Image source: https://medium.com/@brockreece/vue-parent-and-child-lifecycle-hooks-5d6236bd561f

At source page someone in comment warns image author about mentioned VUE notice but does not explain it. It seems that everyone knows about that notice but nobody explains it.

Advertisement

Answer

As @RandyCasburn mentioned, components can be loaded asynchronously. Each property of the components object can either be given a component definition object, or a Promise which eventually resolves with a component definition object. So, in these cases, it could be that the parent component’s mounted hook gets called before the asynchronously-loaded children have finished loading.

As a simple test of this, you could change your base component’s component object to load the child component after a delay:

components: {
  child: () => new Promise((resolve) => {
    setTimeout(() => {
      resolve(child)
    }, 2000)
  }) 
}

You’ll see that the reference to child component in the mounted hook is null when it fires.

It does seem unhelpful that the Vue docs do not mention this as a potential cause for the scenario they are warning against. And, other than using async components, I can’t think of another scenario where a child component would not be mounted when the parent’s mounted hook is fired.


Here’s a full example using your code:

Vue.config.productionTip = false

var subchild = {
  template: `<div>SUBCHILD<span id="subchild"></span></div>`,
  mounted() {
    console.log("Mounted SUBCHILD", document.getElementById('root'), document.getElementById('child'), document.getElementById('subchild'));
  }
};

var child = {
  template: `<div>CHILD<span id="child"></span><subchild></subchild></div>`,
  components : { subchild: () => new Promise((resolve) => {
    setTimeout(() => {
      resolve(subchild)
    }, 2000)
  }) },
  mounted() {
    console.log("Mounted CHILD", document.getElementById('root'), document.getElementById('child'), document.getElementById('subchild'));
  }
};

var app = new Vue({
  el: '#app',
  components : { child: () => new Promise((resolve) => {
    setTimeout(() => {
      resolve(child)
    }, 2000)
  }) },
  data() {
    return { message: 'APP' };
  },
  mounted() {
    console.log("Mounted APP", document.getElementById('root'), document.getElementById('child'), document.getElementById('subchild'));
  }

})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  {{ message }}
  <child ref="child"></child>
  <span id="root"></span>
</div>
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement