admin管理员组文章数量:1207127
1I have a time-table component that is created with vue.js and it includes around 200 child timeline components as nested form (I wanted to upload image but couldn't without 10 reputations).
The problem now is that it takes more than 6 seconds to destroy this component.
Chrome says that 'remove' function (,which is called by vue.js everytime we destroy a component,) is called many times, and each of them takes around 20 - 40ms.
The vue.js remove function is like below:
function remove (arr, item) {
if (arr.length) {
var index = arr.indexOf(item);
if (index > -1) {
return arr.splice(index, 1)
}
}
}
and it seems that the first argument ,arr, is either a few VueComponents or more than 2000 Watcher objects.
Now, my questions are: 1. What is 'Watcher' in this context and why the number it exceeds 2000? 2. Why it takes such long time despite I do not handle like 10000 components or so?
I guess it is the matter of specification of vue.js, but please help me if you have a similar problem or have any idea about this matter. Thank you!
Above is how the timeline component appears, and each of gray-background panels and purple background panel(with a man icon) are child components. When you click a purple panel, vue-router makes routing to the page of the detail, and at that time all of components are destroyed (that is when the problems above occurs)
1I have a time-table component that is created with vue.js and it includes around 200 child timeline components as nested form (I wanted to upload image but couldn't without 10 reputations).
The problem now is that it takes more than 6 seconds to destroy this component.
Chrome says that 'remove' function (,which is called by vue.js everytime we destroy a component,) is called many times, and each of them takes around 20 - 40ms.
The vue.js remove function is like below:
function remove (arr, item) {
if (arr.length) {
var index = arr.indexOf(item);
if (index > -1) {
return arr.splice(index, 1)
}
}
}
and it seems that the first argument ,arr, is either a few VueComponents or more than 2000 Watcher objects.
Now, my questions are: 1. What is 'Watcher' in this context and why the number it exceeds 2000? 2. Why it takes such long time despite I do not handle like 10000 components or so?
I guess it is the matter of specification of vue.js, but please help me if you have a similar problem or have any idea about this matter. Thank you!
Above is how the timeline component appears, and each of gray-background panels and purple background panel(with a man icon) are child components. When you click a purple panel, vue-router makes routing to the page of the detail, and at that time all of components are destroyed (that is when the problems above occurs)
Share Improve this question edited May 10, 2024 at 13:35 Kalnode 11.3k3 gold badges40 silver badges73 bronze badges asked Apr 25, 2019 at 6:18 高田悠高田悠 3232 silver badges7 bronze badges 7 | Show 2 more comments2 Answers
Reset to default 19We have experienced similar issues and found that all share the same underlying problem: too many components that depend on the same reactive object. These are the 3 main cases that may impact any project:
- Many
router-link
components - Many components (any kind) when Vue I18n is installed
- Many components that directly access the Vuex store on its render or computed properties.
Our approach is to avoid accessing shared reactive objects on the render and computed properties functions. Instead, pass them as props
(reactive) or access them on the created
or updated
hooks (not reactive) to store in the component's $data
. Read below for more details and each of the 3 cases.
A brief explanation of Vue 2 reactivity
(skip this if you don't need it)
The Vue reactivity basically relays on two intertwined objects: Watcher and Dep. Watchers have a list of dependencies (Deps) in the deps
attribute, and Deps have a list of dependants (Watchers) in the subs
attribute.
For every reactive thing, Vue instantiates a Dep that tracks reads and writes on it.
Vue instantiates a Watcher for every component (actually, for the render
function) and every Computed Property. The Watchers watch a function during its execution. While watching, if a reactive object is read, the associated Dep notices the Watcher, and they become related: The Watcher.deps
contains the Dep
, and the Dep.subs
contains the Watcher
.
Afterwards, if the reactive thing changes, the associated Dep
notifies all its dependants (Dep.subs
) and tells them to update (Watcher.update
).
When a component is destroyed, all its Watchers are destroyed as well. This process implies iterating each Watcher.deps
to remove the Watcher itself from the Dep.subs
(see Watcher.teardown).
The problem
All the components that depend on the same reactive thing insert a Watcher on the same Dep.subs
. In the following example, the same Dep.subs
contains 10,000 watchers:
- 1,000 items rendered (e.g. a grid, an infinite scroll, ...)
- Each item implies 10 components: itself, 2 router-link, 3 buttons, 4 other (nested and not nested, from your code or third party).
- All components depend on the same reactive object.
When destroying the page, the 10,000 watchers will remove themselves from the Dep.subs
array (one by one). The cost of removing themselves is 10k * O(10k - i)
where i
is the number of watchers already removed.
In general, the cost of removing n
items is O((n^2)/2)
.
Workarounds
In case you render many components, avoid accessing shared reactive dependencies on the render
or computed properties.
Instead, pass them as props
or access them on the created
or updated
hooks and store them on the component's $data
. Bear in mind that the hooks aren't watched so the component won't be updated if the source of data changes, which is still suitable for many cases (any case where the data won't change once the component is mounted).
If your page renders a long list of items, the vue-virtual-scroller is bound to help. In this case, you can still access shared reactive dependencies because the vue-virtual-scroller reuses a small pool of your components (it does not render what is not seen).
Take into account that having thousands of component might be easier than you expect because we tend to write small components and compose them (actually a good practice)
Case: Vuex
If you do something like this in your render o computed property, your component depends on all the chain of reactive things: state
, account
, profile
.
function myComputedProperty() {
this.$store.state.account.profile.name;
}
In this example, if your account does not change once the component is mounted, you can read it from the created
or beforeMount
hook and store the name
on the Vue $data
. As this is not part of the render function nor part of a computed property, there is no Watcher watching the access to the store.
function beforeMount() {
this.$data.userName = this.$store.state.account.profile.name;
}
Case: router-link
See the issue #3500
Case: Vue I18n
This has the same underlying problem but with a bit different explanation. See the issue #926
It's not a vue problem, see on your mixins/options.
Eg. i18n
(my pain) in every of 200 components will show the same result. It removes a lot of watchers on beforeDestroy
. Without i18n
the list works 30 times faster.
How to fix it? Move the slow hook-handlers to the parent component and get needed data/methods from it.
The sample with i18n
Vue.mixin({
beforeCreate() {
if (this.$options.useParentLocalization) {
this._i18n = parent.$i18n;
}
},
});
Usage:
new Vue({
// i18n, <-- before
useParentLocalization: true,
components: {
Component1
}
})
本文标签: javascriptCustom component takes too long to 39destroy39 childrenStack Overflow
版权声明:本文标题:javascript - Custom component takes too long to 'destroy' children - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1738711188a2108203.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
remove
, what iswatcher
that you are talking about. Your current code sample is not enough to figure out anything. – blaz Commented Apr 25, 2019 at 7:11