-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update slot content without rerendering rest of component #6351
Comments
Note: I understand this wouldn't be possible for scoped slots. However, an optimization that might help even for scoped slots would be to only trigger rerender of the child component if the rerender of the parent component generated a delta for the actual slot content. |
Well, I think it would be possible to implement something that would diff slot contents before updateiung the components but - ignoring difficulties in implementing this for the moment, because I can't say much about this right now - it would come with a performance tradeoff: With your proposal, we would save rendering the virtualdom of the child if nothing in the slot changed - Essentially this means that now, children with big templates and small slot contents would run better, while children with small templates and big slots woudl run worse when changes happen. Not sure what is better... Also, technically the child would keep an outdated virtualDOM, because while the content of the slot nodes is the same, the parent created fresh nodes when it re-rendered, so I suspect that this might be a technical hurdle. |
In my case it's a slot that the component uses in a v-for loop to apply to hundreds or more repeats. In this case it's unquestionably faster to calculate the delta. I wonder if this can be reasonably detected. |
Hardly, and if so, only during compile time, not runtime. That would require some analysis during compilation that would have to derminate when a template is "expensive, then set some flag so the component resolts to slot diffing during runtime, and doesn't for cheap components. Sounds easy but measuring "expensiveness" would be very tricky considering A new API would be thinkable to set that flag manually, at least in theory. |
I would say if either:
Then that's a time it's probably worth it to test the delta. In my particular case, it would save me having to make some rather unintuitive changes to the structure of a library that's used by a lot of developers in my company. |
Also, calling the render function is typically much slower than performing a diff on two strings of data. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Would it be possible to introduce a new watcher boundary API? This would be an internal implementation, to which template slot content would compile to. This way, the slot's content wouldn't even have to re-render if the data it depends on doesn't change. Since it doesn't re-render, it doesn't need to be diffed. |
Just to show another use-case of how I bumped into this: Vue's computed properties currently can't take any arguments. Instead, if you want to use some calculated data in a loop (which is generally where you'd need to pass data into a computed), you have to resort to one of these:
Here's demo in action: https://codepen.io/JosephSilber/pen/MXJZro 1 A potential solution would be to have a |
You can create a computed prop that returns an array of total order values for all orders |
@jacekkarczmarczyk the problem with that is that if any of the orders changes, all order totals then have to be recalculated. |
Good news! It seems like this will be resolved in Vue 3.0:
https://medium.com/the-vue-point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf |
will there be a 2.6 update to fix this? I have a basic spreadsheet like app where some slots are overridden with slots representing validation or special formatting of the data. When a user updates a the model within an input and tabs to the next input, the components child slots re-render causing the parent to re-render, and the user's input box loses its focus. |
Any news on this ? it make some code/lib (using a lot slots) unusable. |
@stygmate the just-released v2.6.0-beta.2 includes #9371, which addresses this issue. |
@JosephSilber That doesn't seem to address dynamically created slots, right? We have a lot of forms where the structures are defined by a json document on load, this does mean that the slots are dynamically defined (even if they don't change once loaded in) A big issue the rerender is causing is that certain sub-components fetch extra data, which it's doing on every re-render in this case. |
This short explanation doesn't really explain what exactly you do and mean by "dynamically created slots". I would advise you to join us in the forums @ forum.vuejs.org and open a more in-depth topic explaining your situation there. |
A all A simple example is a custom multi-select where a sum is added <template slot="example" slot-scope="{ field, errors, i18n, locale }">
<div>
<select-field
:errors="errors"
:field="field"
:i18n="i18n"
:locale="locale"
ref="example"
:custom-params="{ repairType: lastParent.id || null }"
@input="serviceSelected"
/>
<span>Total: {{ total }}</span>
</div>
</template> Currently, when the total is updated the whole form is re-rendered, which causes all (server-side) |
any news for this one ? Vuetify (the most stared Vuejs project) seems generate a lot of slot dynamically and performance are really really bad in some case. vuetifyjs/vuetify#6201 |
Would be helpful to have some. Up-to-date example that demonstrates the effect, especially with the new slot syntax. From what I've read I don't entirely get the problem. |
@LinusBorg, it is really quite simple if you have an component with a slot <component>
<slot :value="value" @change="v => value = v" />
</component> Whenever the <component>
<input />
<slot :value="value" @change="v => value = v" />
</component> Set the focus to the |
@LinusBorg I can show you a live project where we are using a lot of PS. Cheers |
@LinusBorg @besnikh exactly the same problem in my app ! |
I see what you mean but I don't really see a via able way to accomplish this except not using a virtualDOM, which means writing a new framework, essentially. You find similar challenges in all vDom based frameworks (react etc.) - to update a slot he whole patent has to re-render in order to determine what to even send to the slot, that's determined by the render function. And nested slots that do a lot wig work on re-renders get expensive if the dependency that's being updated by e.g. an input is being provided by some distant ancestor-component. For the framework that we have, we should rather investigate better patterns to compose our components in order to prevent these deep re-renders. |
@LinusBorg I'm not an expert in virtual DOMs, so I'm interested in doing a 5 whys here:
Why? What's the importance of it being a slot? If it wasn't a slot it would work correctly, and the resultant html and javascript look the same. The parent would get the updated property intentionally and then the child would be afterwards. However, we know something, we know that this isn't a property changing, instead we know it is a slot changing. Soooo.... I'm going to say something that is probably dumb:
<component :props="parent">
<slot-component :props="parent + child">
<input>
</slot-component>
<component> but instead of those inputs |
I'll answer aboutbrh why tomorrow, it's 1am here. But as I see a risk of us talking past each other I would still be thankful for an actually runnable example clearly demonstrating your issue instead of 3 lines from above. The thing about the focus is clearly not the performance issue we are discussing here... |
Solving this seems to require decoupling the scope of rendering from the scope of a component. This will be coming with Vue 3, I think it is highly unlikely in Vue 2. |
Is this fixed now in vue 3? @adamvleggett |
Aside from the optimizations we already introduced to Vue 2 with VirtualDOM implementations rely on this behavior in general. Vue 3 is no different. Vue 3 might offer improvements in terms of performance as we now only need to re-render and diff vDOM for dynamic elements - if you generate large structured of static HTML, those can now be ignored by the renderer due to compiler optimizations. |
@LinusBorg I think I have use-case similar to disclaimerI'm posting it here because I think is related, if you think is not or is simply the way Vue works and I should read more about VDOM topic feel free to mark it as spam — no problem at all 🙏. long readHere's the demo sandbox <template>
<!-- parent -->
<div class="parent">
<slot v-bind="{ binding }"></div>
</div>
</template>
<script>
export default {
name: 'Parent',
data() {
return { binding: 0 }
},
// something that mutates 'binding', omitted for brevity, check demo
}
</script> <template>
<!-- child -->
<div class="child">
<slot>Default slot content</slot>
</div>
</template>
<script>
export default {
name: 'Child',
}
</script> Usage<parent v-slot:default="{ binding }">
<!-- do stuff with binding -->
<pre> binding: {{ binding }}.</pre>
<!-- these do not do anything with "binding" but are re-rendered -->
<child id="1" />
<child id="2">Why am I updated?</child>
</parent> Is it expected that the EDIT: actually updated Real use case
<v-popper #default="{ reference, popper, close }">
<button v-bind="reference.attrs">Some Ref</button>
<div :style="popper.styles">
<!-- re-renders when popper.styles is updated -->
<app-menu>
<app-menu-item>Go to x</app-menu-item>
<app-menu-item @click="close">Dismiss</app-menu-item>
<app-menu-item @click="close">
<app-icon name="pen" />
<span>Edit</span>
</app-menu-item>
</app-menu-item>
</div>
<v-popper> Even if I make wrapper components for the "slots" and orchestrate everything with provide/Inject, passing a reference to a method via slot scope also triggers a re-render. Not using Code example<v-popper-provider>
<v-popper-reference>Some Ref</v-popper-reference>
<v-popper-el v-slot:default="{ close }">
<!-- re-renders on VPopperEl internal style updates -->
<app-menu>
<app-menu-item>El</app-menu-item>
<app-menu-item @click="close" >El</app-menu-item>
</app-menu-item>
</v-popper-el>
<v-popper-provider> The alternative is to use $refs and call method <v-popper-provider ref="popper">
<v-popper-reference>Some Ref</v-popper-reference>
<v-popper-el >
<!-- no scoped slot, no re-render, but refs feels like a dirty workaround -->
<app-menu>
<app-menu-item>El</app-menu-item>
<app-menu-item @click="$refs.popper.close">El</app-menu-item>
</app-menu-item>
</v-popper-el>
<v-popper-provider> |
Some articles introduce the use of 'renderless' component to provide e.g. data binding. I guess it's not recommended due to this issue? (if the slot is large) |
UP. |
What problem does this feature solve?
I have developed some components that can generate large amounts of HTML, and allow content to be added via slot. It appears that if the slot content is updated, the render function is called for the component; however, this seems like something that could be avoided through optimization which would significantly improve the performance of my component in some instances.
Is this possible?
What does the proposed API look like?
Not proposing API changes.
The text was updated successfully, but these errors were encountered: