summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/five-walls-build.md5
-rw-r--r--packages/astro/e2e/fixtures/view-transitions/src/components/VueCounter.vue12
-rw-r--r--packages/astro/e2e/fixtures/view-transitions/src/pages/island-vue-one.astro11
-rw-r--r--packages/astro/e2e/fixtures/view-transitions/src/pages/island-vue-two.astro11
-rw-r--r--packages/astro/e2e/view-transitions.test.js20
-rw-r--r--packages/integrations/vue/client.js46
6 files changed, 87 insertions, 18 deletions
diff --git a/.changeset/five-walls-build.md b/.changeset/five-walls-build.md
new file mode 100644
index 000000000..41f89aa3e
--- /dev/null
+++ b/.changeset/five-walls-build.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/vue': patch
+---
+
+Fix vue islands keeping their state when using view transition persistence
diff --git a/packages/astro/e2e/fixtures/view-transitions/src/components/VueCounter.vue b/packages/astro/e2e/fixtures/view-transitions/src/components/VueCounter.vue
index e75620aff..e05cf6a14 100644
--- a/packages/astro/e2e/fixtures/view-transitions/src/components/VueCounter.vue
+++ b/packages/astro/e2e/fixtures/view-transitions/src/components/VueCounter.vue
@@ -1,8 +1,8 @@
<template>
<div class="counter">
- <button @click="subtract()">-</button>
- <pre>{{ count }}</pre>
- <button @click="add()">+</button>
+ <button @click="subtract()" class="decrement">-</button>
+ <pre>{{prefix}}{{ count }}</pre>
+ <button @click="add()" class="increment">+</button>
</div>
<div class="counter-message">
<slot />
@@ -12,6 +12,12 @@
<script lang="ts">
import { ref } from 'vue';
export default {
+ props: {
+ prefix: {
+ type: String,
+ default: '',
+ },
+ },
setup() {
const count = ref(0);
const add = () => (count.value = count.value + 1);
diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/island-vue-one.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/island-vue-one.astro
new file mode 100644
index 000000000..b8ce5e48d
--- /dev/null
+++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/island-vue-one.astro
@@ -0,0 +1,11 @@
+---
+import Layout from '../components/Layout.astro';
+import Counter from '../components/VueCounter.vue';
+export const prerender = false;
+
+---
+<Layout>
+ <p id="island-one">Page 1</p>
+ <a id="click-two" href="/island-vue-two">go to 2</a>
+ <Counter prefix="AA" client:load transition:persist transition:name="counter" />
+</Layout>
diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/island-vue-two.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/island-vue-two.astro
new file mode 100644
index 000000000..5c688be79
--- /dev/null
+++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/island-vue-two.astro
@@ -0,0 +1,11 @@
+---
+import Layout from '../components/Layout.astro';
+import Counter from '../components/VueCounter.vue';
+export const prerender = false;
+
+---
+<Layout>
+ <p id="island-two">Page 2</p>
+ <a id="click-two" href="/island-vue-one">go to 1</a>
+ <Counter prefix="BB" client:load transition:persist transition:name="counter" />
+</Layout>
diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js
index 181994cfa..f1ced26c7 100644
--- a/packages/astro/e2e/view-transitions.test.js
+++ b/packages/astro/e2e/view-transitions.test.js
@@ -522,7 +522,7 @@ test.describe('View Transitions', () => {
expect(secondTime).toBeGreaterThanOrEqual(firstTime);
});
- test('Islands can persist using transition:persist', async ({ page, astro }) => {
+ test('React Islands can persist using transition:persist', async ({ page, astro }) => {
// Go to page 1
await page.goto(astro.resolveUrl('/island-one'));
let cnt = page.locator('.counter pre');
@@ -544,6 +544,24 @@ test.describe('View Transitions', () => {
await expect(pageTitle).toHaveText('Island 2');
});
+ test('Vue Islands can persist using transition:persist', async ({ page, astro }) => {
+ // Go to page 1
+ await page.goto(astro.resolveUrl('/island-vue-one'));
+ let cnt = page.locator('.counter pre');
+ await expect(cnt).toHaveText('AA0');
+
+ await page.click('.increment');
+ await expect(cnt).toHaveText('AA1');
+
+ // Navigate to page 2
+ await page.click('#click-two');
+ const p = page.locator('#island-two');
+ await expect(p).toBeVisible();
+ cnt = page.locator('.counter pre');
+ // Count should remain, but the prefix should be updated
+ await expect(cnt).toHaveText('BB1');
+ });
+
test('transition:persist-props prevents props from changing', async ({ page, astro }) => {
// Go to page 1
await page.goto(astro.resolveUrl('/island-one?persist'));
diff --git a/packages/integrations/vue/client.js b/packages/integrations/vue/client.js
index 807f843fc..58ee11d5c 100644
--- a/packages/integrations/vue/client.js
+++ b/packages/integrations/vue/client.js
@@ -2,6 +2,9 @@ import { setup } from 'virtual:@astrojs/vue/app';
import { Suspense, createApp, createSSRApp, h } from 'vue';
import StaticHtml from './static-html.js';
+// keep track of already initialized apps, so we don't hydrate again for view transitions
+let appMap = new WeakMap();
+
export default (element) =>
async (Component, props, slotted, { client }) => {
if (!element.hasAttribute('ssr')) return;
@@ -15,21 +18,36 @@ export default (element) =>
const isHydrate = client !== 'only';
const bootstrap = isHydrate ? createSSRApp : createApp;
- const app = bootstrap({
- name,
- render() {
- let content = h(Component, props, slots);
- // related to https://github.com/withastro/astro/issues/6549
- // if the component is async, wrap it in a Suspense component
- if (isAsync(Component.setup)) {
- content = h(Suspense, null, content);
- }
- return content;
- },
- });
- await setup(app);
- app.mount(element, isHydrate);
+ // keep a reference to the app, props and slots so we can update a running instance later
+ let appInstance = appMap.get(element);
+
+ if (!appInstance) {
+ appInstance = {
+ props,
+ slots,
+ };
+ const app = bootstrap({
+ name,
+ render() {
+ let content = h(Component, appInstance.props, appInstance.slots);
+ appInstance.component = this;
+ // related to https://github.com/withastro/astro/issues/6549
+ // if the component is async, wrap it in a Suspense component
+ if (isAsync(Component.setup)) {
+ content = h(Suspense, null, content);
+ }
+ return content;
+ },
+ });
+ await setup(app);
+ app.mount(element, isHydrate);
+ appMap.set(element, appInstance);
+ } else {
+ appInstance.props = props;
+ appInstance.slots = slots;
+ appInstance.component.$forceUpdate();
+ }
element.addEventListener('astro:unmount', () => app.unmount(), { once: true });
};