vuejs / core

🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
https://vuejs.org/
MIT License
47.36k stars 8.28k forks source link

Slots in loops only assigned once when component used as custom element #10691

Open sawmurai opened 6 months ago

sawmurai commented 6 months ago

Vue version

3.4.21

Link to minimal reproduction

https://github.com/sawmurai/vue-custom-elements-slots-reproducer

Steps to reproduce

What is expected?

The slot-content, rendered three times, should show the replacement, three times.

What is actually happening?

The slot-content, rendered three times, is only replaced once.

System Info

System:
    OS: Linux 6.5 Ubuntu 22.04.3 LTS 22.04.3 LTS (Jammy Jellyfish)
    CPU: (16) x64 AMD Ryzen 7 3800X 8-Core Processor
    Memory: 56.84 GB / 62.67 GB
    Container: Yes
    Shell: 3.7.0 - /usr/bin/fish
  Binaries:
    Node: 20.12.1 - ~/.nvm/versions/node/v20.12.1/bin/node
    Yarn: 1.22.19 - ~/.yarn/bin/yarn
    npm: 10.5.0 - ~/.nvm/versions/node/v20.12.1/bin/npm
    pnpm: 8.15.6 - ~/.nvm/versions/node/v20.12.1/bin/pnpm
    bun: 1.0.0 - ~/.bun/bin/bun
  Browsers:
    Chrome: 121.0.6167.160
    Chromium: 123.0.6312.86

Any additional comments?

I think to fix this problem the slot assignment needs to be done manually (by setting the slotAssignment to manual). I played with that option and was able to get a rough version working (I cloned the <div slot="root"> elements and assigned them manually to each instance of the slot in the DOM). Of course, the devil is in the details and I did not even look at edge cases. Before attempting a full-blown PR, I would like your feedback, if this is something you even want to support, first.

sawmurai commented 6 months ago

I investigated some more and created a fix locally. Unfortunately I am not able to add a test because jsdom does not yet support the imperative slot API (see this issue and this PR). I would also have to disable the test for the default slot assignment as it needs HTMLSlotElement.assign as well (since the default assignment would not be used anymore).

If you like I can still push the branch and create a PR.

yyx990803 commented 6 months ago

HTMLSlotElement.assign is relatively new and doesn't align with Vue's baseline support (browsers that support ES2016+), so it might be problematic to use it directly.

Looks like it can be polyfilled, although we might be able to implement only what is needed by Vue?

sawmurai commented 6 months ago

Another idea would be to keep the automated assignment but update the slot names dynamically: After (or perhaps during?) rendering, check if a slot name is used more than once and make the copies of that slot name unique (the name attribute of the slot tags). Then, duplicate the slottables and assign the same unique names to the slot attributes. This way baseline support is respected and no polyfill is needed.

Edit: This appears to be very similar to what the polyfill you linked would do