I'm trying to create a Vue ponents instance programmatically and I got some problems.
<!DOCTYPE html>
<html>
<head>
<script src=".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 :
- 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 appendvm.$el
. While the fact is that it is called right aftervm.$mount()
- Why
this.$el.getBoundingClientRect().height
get 0 if it is not putted in vm.$nextTick function ? - 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 meandocument.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.
I'm trying to create a Vue ponents instance programmatically and I got some problems.
<!DOCTYPE html>
<html>
<head>
<script src="http://vuejs/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 :
- 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 appendvm.$el
. While the fact is that it is called right aftervm.$mount()
- Why
this.$el.getBoundingClientRect().height
get 0 if it is not putted in vm.$nextTick function ? - 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 meandocument.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.
-
1
vuejs/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 Commented Aug 27, 2019 at 11:13
2 Answers
Reset to default 3This 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 ponents 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 ponent or the DOM isn't rendered with you call getBoundingClientRect()
See also Vue - is there any way to tie into render being finished?
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')
.