a component that passes slots to children forces children to rerender
Version
3.2.31
Reproduction link
Steps to reproduce
I've found some cases that shouldn't makes slot-component to update, but it does. Only case #1 works properly. It looks like a bug, because it affects at lot of cases when you want to pass slots through a component to a children with this technique.
Vue.component('W', {
props: ['child'],
template: `<component :is="child">
<template v-for="(_, name) in $slots" v-slot:[name]="slotData"><slot :name="name" v-bind="slotData" /></template>
</component>`
})
What is expected?
no updates in children component
What is actually happening?
it does update
Test 2-4 are updating because you pass already-rendered vnodes to the child, instead of a stable slot function.
However, we generally can't give 100% guarantee that all slot functions only re-render in case one of their dependencies changes. The compiler can't always be sure that a slot only depends on its own content, especially when it comes to nested slots.
So it sometimes it has to de-optimize for consistency and have the parents update their children alongside.
All That being said, Test 5 seems unnecessary so it's worth a look, but I would not be suprised if its because of the nested slot.
Thank you for your explanation. I rewrote test 2 according to official documentation. Those 2 examples are equivalent (according to docs), but the case with a render function still makes the child component re-render. Bug?
<InnerChild> <template #test="scope"> {{ scope.foo }} </template> </InnerChild>
() => <InnerChild>{{ foo: (scope) => scope.foo }}</InnerChild>
.
And it would be nice if we could just pass slots to children this way without loosing an optimization.
setup(props, { slots }) { return () => h( InnerChild, { }, slots, ) }
Because now the best solution of this problem to avoid using slots API and pass render functions with props.
I must have been sleep-deprived when I wrote my initial reply as none of the the examples in the repo are actually passing pre-rendered vnodes. Sorry.
Will need to take a closer look, but likely, Evan will have to dig into this as this part of the codebase is pretty complex in terms of interactions that are happening.
But my general point still stands - we sometimes can't ensure that a slot is actually stable and so parents have to trigger re-renders in children. This is more often the case when using pure render functions as those don't contain the compiler-generated optimization hints.
Maybe there's room for improvement in providing helpers to add those manually.
I met same problem. I created repository for reproducing.
Demo shows input with available clickable options. If you click on one of element, you'll see that update hook will called on each element. (It shows on page Children updated and in console).
If remove slot tag in components/MyDropdownList.vue
, children wont's update.
On big lists and nested templates with component-like this in my project I have very low performance.
Maybe someone has workaround?
@bonilka to workaround this problem you always can avoid using slot api, just pass render function that would return VNode and render this VNode in a component template
https://stackoverflow.com/questions/49352525/can-you-render-vnodes-in-a-vue-template