0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-30 22:03:56 -05:00

fix(vue): useId() collisions (#12112)

This commit is contained in:
Florian Lefebvre 2024-10-03 20:52:11 +02:00 committed by GitHub
parent 34d79527a4
commit f9dd9428c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 60 additions and 1 deletions

View file

@ -0,0 +1,5 @@
---
'@astrojs/vue': patch
---
Fixes a case where IDs generated by `useId()` (introduced in Vue 3.5) would not be unique between islands

View file

@ -40,6 +40,7 @@ export default (element) =>
return content;
},
});
app.config.idPrefix = element.getAttribute('prefix');
await setup(app);
app.mount(element, isHydrate);
appMap.set(element, appInstance);

View file

@ -0,0 +1,24 @@
const contexts = new WeakMap();
const ID_PREFIX = 'v';
function getContext(rendererContextResult) {
if (contexts.has(rendererContextResult)) {
return contexts.get(rendererContextResult);
}
const ctx = {
currentIndex: 0,
get id() {
return ID_PREFIX + this.currentIndex.toString();
},
};
contexts.set(rendererContextResult, ctx);
return ctx;
}
export function incrementId(rendererContextResult) {
const ctx = getContext(rendererContextResult);
const id = ctx.id;
ctx.currentIndex++;
return id;
}

View file

@ -2,12 +2,19 @@ import { setup } from 'virtual:@astrojs/vue/app';
import { createSSRApp, h } from 'vue';
import { renderToString } from 'vue/server-renderer';
import StaticHtml from './static-html.js';
import { incrementId } from './context.js';
function check(Component) {
return !!Component['ssrRender'] || !!Component['__ssrInlineRender'];
}
async function renderToStaticMarkup(Component, inputProps, slotted, metadata) {
let prefix;
if (this && this.result) {
prefix = incrementId(this.result);
}
const attrs = { prefix };
const slots = {};
const props = { ...inputProps };
delete props.slot;
@ -21,9 +28,10 @@ async function renderToStaticMarkup(Component, inputProps, slotted, metadata) {
});
}
const app = createSSRApp({ render: () => h(Component, props, slots) });
app.config.idPrefix = prefix;
await setup(app);
const html = await renderToString(app);
return { html };
return { html, attrs };
}
export default {

View file

@ -30,4 +30,13 @@ describe('Basics', () => {
assert.notEqual(img, undefined);
assert.equal(img.getAttribute('src'), '/light_walrus.avif');
});
it('Should generate unique ids when using useId()', async () => {
const data = await fixture.readFile('/index.html');
const { document } = parseHTML(data);
const els = document.querySelectorAll('.vue-use-id');
assert.equal(els.length, 2);
assert.notEqual(els[0].getAttribute('id'), els[1].getAttribute('id'));
});
});

View file

@ -0,0 +1,9 @@
<script setup>
import { useId } from "vue"
const id = useId()
</script>
<template>
<p class="vue-use-id" :id="id">{{ id }}</p>
</template>

View file

@ -1,6 +1,7 @@
---
import Bar from '../components/Foo.vue';
import Parent from '../components/Parent.astro';
import WithId from '../components/WithId.vue';
---
<html>
<head>
@ -10,5 +11,7 @@ import Parent from '../components/Parent.astro';
<Parent>
<Bar slot="footer" />
</Parent>
<WithId client:idle />
<WithId client:idle />
</body>
</html>