Suppose I have an input field in Vue.JS that v-model
bind to a String data property, and a long list of random numbers that are pletely unrelated to that first String.
data: {
input: "",
randoms: []
}
<input type="text" v-model="input">
<p v-for="random in randoms" v-text="random"></p>
When I put both in the same Vue, I see a huge slowdown when typing in the input field, as it appears Vue is reevaluating the DOM for each list entry after every input event, although they really have nothing to do with each other.
/
When I however move the v-for
to a child ponent where I bind randoms
to a prop, I experience no such slowdown
/
Is there a way I can achieve the performance of the second fiddle without using a child-ponent?
Suppose I have an input field in Vue.JS that v-model
bind to a String data property, and a long list of random numbers that are pletely unrelated to that first String.
data: {
input: "",
randoms: []
}
<input type="text" v-model="input">
<p v-for="random in randoms" v-text="random"></p>
When I put both in the same Vue, I see a huge slowdown when typing in the input field, as it appears Vue is reevaluating the DOM for each list entry after every input event, although they really have nothing to do with each other.
https://jsfiddle/5jf3fmb8/2/
When I however move the v-for
to a child ponent where I bind randoms
to a prop, I experience no such slowdown
https://jsfiddle/j601cja8/1/
Is there a way I can achieve the performance of the second fiddle without using a child-ponent?
Share Improve this question asked Dec 8, 2016 at 15:08 Maximilian SchierMaximilian Schier 1,68915 silver badges20 bronze badges 1-
You could use debouncing, causing the
input
variable to be updated less often. Updated fiddle: jsfiddle/asemahle/5jf3fmb8/3 – asemahle Commented Dec 8, 2016 at 16:07
2 Answers
Reset to default 12Is there a way I can achieve the performance of the second fiddle without using a child-ponent?
Short answer
No.
Long answer
Whenever any dependency of the template changes, Vue has to re-run the render function for the entire ponent and diff the new virtualDOM against the new one. It can't do this for this or that part of the template only, and skip the rest. Therefore, each time the input value changes, the entire virutalDOM is re-rendered.
Since your v-for is producing quite a bit of elements, this can take a few 100ms, enough to be noticable when you type.
Extracting the heavy part of the template into its own ponent is in fact the "official" way to optimize that.
As Alex explained, v-model.lazy might improve the situation a bit, but does not fix the core of the issue.
Shortest, simplest answer: change v-model
to v-model.lazy
.
When I put both in the same Vue, I see a huge slowdown when typing in the input field, as it appears Vue is reevaluating the DOM for each list entry after every input event, although they really have nothing to do with each other.
Note that the OnceFor sample still chugs like mad despite not actually being reactive any more. I don't understand Vue well enough to say if that's intentional or not.
const Example = {
data() { return { input: "", randoms: [] } },
created() { this.newRandoms() },
methods: {
newRandoms() { this.randoms = Array(50000).fill().map(() => Math.random()) }
}
}
new Vue({
el: "#vue-root",
data(){ return {example: 'lazy-model'}},
ponents: {
LazyModel: {...Example, template: "#lazy-model"
},
OnceFor: {...Example, template: "#once-for"
},
InlineTemplate: {...Example, template: "#inline-template",
ponents: {
Welp: {
props: ['randoms']
}
}
}
}
})
button,
input,
div {
margin: 2px;
}
<script src="https://unpkg./vue/dist/vue.js"></script>
<div id="vue-root">
<span><button v-for="(ponent, name) in $options.ponents" @click="$set($data, 'example', name)">{{name}}</button></span>
<ponent :is="example"></ponent>
</div>
<template id="lazy-model">
<div>
<input type="text" v-model.lazy="input"><br>
<input type="submit" value="Regenerate" @click="newRandoms">
<p v-for="random of randoms" v-text="random"></p>
</div>
</template>
<template id="once-for">
<div>
<input type="text" v-model="input"><br>
<input type="submit" value="Regenerate" @click="newRandoms">
<p v-for="random of randoms" v-text="random" v-once></p>
</div>
</template>
<template id="inline-template">
<div>
<input type="text" v-model="input"><br>
<input type="submit" value="Regenerate" @click="newRandoms">
<welp :randoms="randoms" inline-template>
<div>
<p v-for="(random, index) of randoms" :key="index"> {{index}}: {{random}} </p>
</div>
</welp>
</div>
</template>