3

I'm trying to create a Vue components instance programmatically and I got some problems.

<!DOCTYPE html>
<html>
  <head>
    <script src="http://vuejs.org/js/vue.js"></script>
    <meta charset="utf-8">
  </head>
  <body>
    <script>
      var vm = new Vue({
        data:{
          mes:'hello vue'
        },
        template:'<div>{{mes}}</div>',
        mounted(){
          console.log(this.$el.getBoundingClientRect().height) // get 0
          this.$nextTick(()=>{
            console.log(this.$el.getBoundingClientRect().height) //get height
          })
        }
      }).$mount()
      document.body.appendChild(vm.$el)
    <script>
  </body>
</html>

My question :

  1. According to docs about the Vue lifecycle, the mounted hooks function should be called after " Create vm.$el and replace 'el' with it ". So I just thought the mounted hooks function should be called after document.body append vm.$el . While the fact is that it is called right after vm.$mount()
  2. Why this.$el.getBoundingClientRect().height get 0 if it is not putted in vm.$nextTick function ?
  3. As far as I know, $nextTick function created a microtask, while the browser UI render should be a macro task. So I don't know why we get the correct height if it is in $nextTick function.
    The DOM height should be calculated after it is putted in the page ( I mean document.body.appendChild(vm.$el))?
    I thought microtask get handled before macro task, so even in $nextTick function we shouldn't get the right height. But it did. I wonder why.
Asef Hossini
  • 655
  • 8
  • 11
Archsx
  • 774
  • 1
  • 11
  • 19
  • https://vuejs.org/v2/api/#mounted Note this bit -> `If you want to wait until the entire view has been rendered, you can use vm.$nextTick inside of mounted:` – Keith Aug 27 '19 at 11:13

2 Answers2

2

This part of the mounted API docs might be the explanation (emphasis mine):

Called after the instance has been mounted, where el is replaced by the newly created vm.$el. If the root instance is mounted to an in-document element, vm.$el will also be in-document when mounted is called.

Note that mounted does not guarantee that all child components have also been mounted. If you want to wait until the entire view has been rendered, you can use vm.$nextTick inside of mounted

Regarding $nextTick():

Defer the callback to be executed after the next DOM update cycle. Use it immediately after you’ve changed some data to wait for the DOM update.

So either you have a child component or the DOM isn't rendered with you call getBoundingClientRect()

See also Vue - is there any way to tie into render being finished?

tony19
  • 125,647
  • 18
  • 229
  • 307
bernie
  • 9,820
  • 5
  • 62
  • 92
0

The problem is that when you call mount you aren't telling it where to mount the $el. The mounted hook will be called before you get to the document.body.appendChild(vm.$el), whereas the $nextTick will be after.

You shouldn't really be appending $el to the DOM yourself. Instead change your HTML to have a suitable target element:, e.g.:

<body>
    <div id="app"></div>
    <script>

Here I've inserted a <div> as the first child of the <body>, immediately before your <script>, to ensure the element exists before the code inside <script> is run.

Then mount the Vue instance either using el: '#app' or by changing the mount() call to mount('#app').

skirtle
  • 27,868
  • 4
  • 42
  • 57