Skip to content
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

Deep (nested) <slot /> in Vue component not rendering when hydrated #6669

Closed
1 task done
spacedawwwg opened this issue Mar 27, 2023 · 12 comments · Fixed by #7093
Closed
1 task done

Deep (nested) <slot /> in Vue component not rendering when hydrated #6669

spacedawwwg opened this issue Mar 27, 2023 · 12 comments · Fixed by #7093
Assignees
Labels
- P3: minor bug An edge case that only affects very specific usage (priority)

Comments

@spacedawwwg
Copy link

spacedawwwg commented Mar 27, 2023

What version of astro are you using?

2.1.7

Are you using an SSR adapter? If so, which one?

None

What package manager are you using?

npm

What operating system are you using?

Mac

What browser are you using?

Chrome, Firefox

Describe the Bug

Deep <slot /> in Vue component not rendering when hydrated when nested within another Vue component that isn't hydrated.

It looks like it was an issue in v1 last year, as mentioned here in this issue #4970

But it also looks like @matthewp said it should be fixed in this comment #4970 (comment), though looking at the stackblitz he linked to in that comment, the same "Hydration completed but contains mismatches." is happening.

I've put together a reproduction show what works and does not work here: https://stackblitz.com/edit/github-f6ayhq-eqxoez

The expectation is that a Vue component with a slot that isn't hydrated should simply render as HTML and the nested Vue component that should hydrate renders without mismatch.

Link to Minimal Reproducible Example

https://stackblitz.com/edit/github-f6ayhq-eqxoez

Participation

  • I am willing to submit a pull request for this issue.
@spacedawwwg
Copy link
Author

spacedawwwg commented Mar 27, 2023

Willing to add a PR if the issue in the astro code can be pointed out 😄

@spacedawwwg spacedawwwg changed the title Deep <slot /> in Vue component not rendering when hydrated Deep (nested) <slot /> in Vue component not rendering when hydrated Mar 27, 2023
@matthewp matthewp added the - P3: minor bug An edge case that only affects very specific usage (priority) label Mar 27, 2023
@JerryWu1234
Copy link
Contributor

image
Why I can do it ?

@spacedawwwg
Copy link
Author

image Why I can do it ?

@wulinsheng123 if you read the issue at the top of that demo

"Issue: "Slot Text!" paragraph should be visible in every example below"

as you can see in your screengrab, the final two counters have no slot text rendering when they should

@JerryWu1234
Copy link
Contributor

image Why I can do it ?

@wulinsheng123 if you read the issue at the top of that demo

"Issue: "Slot Text!" paragraph should be visible in every example below"

as you can see in your screengrab, the final two counters have no slot text rendering when they should

thank for you explain

@spacedawwwg
Copy link
Author

@matthewp is there any chance of you pointing me in the right direction of what code I could be looking at to work on a PR resolve this? Thanks

@JerryWu1234
Copy link
Contributor

@matthewp is there any chance of you pointing me in the right direction of what code I could be looking at to work on a PR resolve this? Thanks

in core folder I think

@spacedawwwg
Copy link
Author

@matthewp is there any chance of you pointing me in the right direction of what code I could be looking at to work on a PR resolve this? Thanks

in core folder I think

Do you think it's a core rendering issue or a vue renderer issue? Not being familiar with the code base makes it a little hard to know where to begin debugging

@spacedawwwg
Copy link
Author

From what I can gather in my reproduction, the only difference is that it seems to not be rendering the <astro-slot> in the HTML around the nested slot content

@spacedawwwg
Copy link
Author

async function renderToStaticMarkup(Component, props, slotted) {

here, I am logging out the value of slotted

in the second entry, you can see the <astro-slot> exists around <p>Slot Text!</p> but in the third entry <astro-slot> is being removed/not added

{
  default: [String (SlotString): '\n\t\t\t\t\t\t<p>Slot Text!</p>\n\t\t\t\t\t'] [HTMLString] {
    instructions: null,
    [Symbol(astro:slot-string)]: true
  }
}
{
  default: [String (SlotString): '\n' +
    '\t\t\t\t\t<astro-island uid="IyM0O" component-url="/src/components/VueCounter.vue" component-export="default" renderer-url="/node_modules/.vite/deps/@astrojs_vue_client__js.js?v=6f754f08" props="{}" ssr="" client="load" before-hydration-url="/@id/astro:scripts/before-hydration.js" opts="{&quot;name&quot;:&quot;VueCounter&quot;,&quot;value&quot;:true}" await-children=""><div class="counter"><div class="counter-tool"><button>-</button><pre>0</pre><button>+</button></div><div class="counter-message"><!--[--><astro-slot>\n' +
    '\t\t\t\t\t\t<p>Slot Text!</p>\n' +
    '\t\t\t\t\t</astro-slot><!--]--></div></div></astro-island>\n' +
    '\t\t\t\t'] [HTMLString] {
    instructions: [ [Object] ],
    [Symbol(astro:slot-string)]: true
  }
}
{
  default: [String (SlotString): '\n' +
    '\t\t\t\t<div class="vue-slot"><!--[-->\n' +
    '\t\t\t\t\t<astro-island uid="IyM0O" component-url="/src/components/VueCounter.vue" component-export="default" renderer-url="/node_modules/.vite/deps/@astrojs_vue_client__js.js?v=6f754f08" props="{}" ssr="" client="load" before-hydration-url="/@id/astro:scripts/before-hydration.js" opts="{&quot;name&quot;:&quot;VueCounter&quot;,&quot;value&quot;:true}" await-children=""><div class="counter"><div class="counter-tool"><button>-</button><pre>0</pre><button>+</button></div><div class="counter-message"><!--[-->\n' +
    '\t\t\t\t\t\t<p>Slot Text!</p>\n' +
    '\t\t\t\t\t<!--]--></div></div></astro-island>\n' +
    '\t\t\t\t<!--]--></div>\n' +
    '\t\t\t'] [HTMLString] {
    instructions: [ [Object] ],
    [Symbol(astro:slot-string)]: true
  }
}

@spacedawwwg
Copy link
Author

spacedawwwg commented May 11, 2023

@matthewp @wulinsheng123 I think I've found the culprit

yield markHTMLString(html.replace(/\<\/?astro-slot\b[^>]*>/g, ''));

if I change this line to simply:

yield markHTMLString(html);

...then the slot content hydrates correctly as the <astro-slot> has not been removed

@matthewp
Copy link
Contributor

matthewp commented May 11, 2023

Yes, that is indeed the issue. But unfortunately those astro-slot elements are needed for hydration. So the issue occurs when there are nested hydrated components inside of other framework components.

This is the same underlying bug as #6597 which I'm hoping to look at soon. I wish I had an answer as to how to fix it but I don't quite know yet. Your example is a bit of a wrinkle because the other is also hydrated.

@spacedawwwg
Copy link
Author

spacedawwwg commented May 11, 2023

@matthewp what about something like this?

let processedHtml = html;
const islandRegex = /<astro-island.+?>.+?<\/astro-island>/gsm;
const islandMatches = processedHtml.match(islandRegex);
// astro-islands are replaced with a unique placeholder to avoid nested slots being removed
if (islandMatches) {
  islandMatches.forEach(function (islandMatch, i) {
    processedHtml = processedHtml.replace(islandMatch, `<astro-island-placeholder-${i} />`);
  });
}
// remove remaining astro-slot elements
processedHtml = processedHtml.replace(/\<\/?astro-slot\b[^>]*>/g, "");
// replace placeholders with original astro-island elements
if (islandMatches) {
  islandMatches.forEach(function (islandMatch, i) {
    processedHtml = processedHtml.replace(`<astro-island-placeholder-${i} />`, islandMatch);
  });
}
yield markHTMLString(processedHtml);

image
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
- P3: minor bug An edge case that only affects very specific usage (priority)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants