feat(nuxt): expose refresh on islands and server components (#24261)

This commit is contained in:
Julien Huang 2023-12-14 12:07:54 +01:00 committed by GitHub
parent 1711c33be4
commit 17b5ed9ad8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 5 deletions

View File

@ -50,3 +50,9 @@ Every slot is interactive since the parent component is the one providing it.
Some slots are reserved to `NuxtIsland` for special cases. Some slots are reserved to `NuxtIsland` for special cases.
- `#fallback`: Specify the content to be rendered before the island loads (if the component is lazy) or if `NuxtIsland` fails to fetch the component. - `#fallback`: Specify the content to be rendered before the island loads (if the component is lazy) or if `NuxtIsland` fails to fetch the component.
## Ref
- `refresh()`
- **type**: `() => Promise<void>`
- **description**: force refetch the server component by refetching it.

View File

@ -46,7 +46,7 @@ export default defineComponent({
default: () => undefined default: () => undefined
} }
}, },
async setup (props, { slots }) { async setup (props, { slots, expose }) {
const error = ref<unknown>(null) const error = ref<unknown>(null)
const config = useRuntimeConfig() const config = useRuntimeConfig()
const nuxtApp = useNuxtApp() const nuxtApp = useNuxtApp()
@ -160,6 +160,10 @@ export default defineComponent({
} }
} }
expose({
refresh: () => fetchComponent(true)
})
if (import.meta.hot) { if (import.meta.hot) {
import.meta.hot.on(`nuxt-server-component:${props.name}`, () => { import.meta.hot.on(`nuxt-server-component:${props.name}`, () => {
fetchComponent(true) fetchComponent(true)

View File

@ -1,4 +1,4 @@
import { defineComponent, h } from 'vue' import { defineComponent, h, ref } from 'vue'
import NuxtIsland from '#app/components/nuxt-island' import NuxtIsland from '#app/components/nuxt-island'
/*@__NO_SIDE_EFFECTS__*/ /*@__NO_SIDE_EFFECTS__*/
@ -7,12 +7,19 @@ export const createServerComponent = (name: string) => {
name, name,
inheritAttrs: false, inheritAttrs: false,
props: { lazy: Boolean }, props: { lazy: Boolean },
setup (props, { attrs, slots }) { setup (props, { attrs, slots, expose }) {
const islandRef = ref<null | typeof NuxtIsland>(null)
expose({
refresh: () => islandRef.value?.refresh()
})
return () => { return () => {
return h(NuxtIsland, { return h(NuxtIsland, {
name, name,
lazy: props.lazy, lazy: props.lazy,
props: attrs props: attrs,
ref: islandRef
}, slots) }, slots)
} }
} }

View File

@ -1,5 +1,5 @@
import { describe, expect, it, vi } from 'vitest' import { describe, expect, it, vi } from 'vitest'
import { h } from 'vue' import { h, nextTick } from 'vue'
import { mountSuspended } from '@nuxt/test-utils/runtime' import { mountSuspended } from '@nuxt/test-utils/runtime'
import { createServerComponent } from '../../packages/nuxt/src/components/runtime/server-component' import { createServerComponent } from '../../packages/nuxt/src/components/runtime/server-component'
import { createSimpleRemoteIslandProvider } from '../fixtures/remote-provider' import { createSimpleRemoteIslandProvider } from '../fixtures/remote-provider'
@ -65,4 +65,35 @@ describe('runtime server component', () => {
await server.close() await server.close()
}) })
it('force refresh', async () => {
let count = 0
const stubFetch = vi.fn(() => {
count++
return {
id: '123',
html: `<div>${count}</div>`,
state: {},
head: {
link: [],
style: []
},
json() {
return this
}
}
})
vi.stubGlobal('fetch', stubFetch)
const component = await mountSuspended(createServerComponent('dummyName'))
expect(fetch).toHaveBeenCalledOnce()
expect(component.html()).toBe('<div>1</div>')
await component.vm.$.exposed!.refresh()
expect(fetch).toHaveBeenCalledTimes(2)
await nextTick()
expect(component.html()).toBe('<div>2</div>')
vi.mocked(fetch).mockRestore()
})
}) })