fix(nuxt): ensure provide / inject work in setup of defineNuxtComponent (#30982)

This commit is contained in:
Alex Liu 2025-02-20 23:20:32 +08:00 committed by GitHub
parent 6d9ddff209
commit b65dfbc98e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 101 additions and 6 deletions

View File

@ -2,7 +2,7 @@ import { getCurrentInstance, reactive, toRefs } from 'vue'
import type { DefineComponent, defineComponent } from 'vue'
import { useHead } from '@unhead/vue'
import type { NuxtApp } from '../nuxt'
import { useNuxtApp } from '../nuxt'
import { getNuxtAppCtx, useNuxtApp } from '../nuxt'
import { useAsyncData } from './asyncData'
import { useRoute } from './router'
import { createError } from './error'
@ -32,7 +32,7 @@ async function runLegacyAsyncData (res: Record<string, any> | Promise<Record<str
export const defineNuxtComponent: typeof defineComponent =
function defineNuxtComponent (...args: any[]): any {
const [options, key] = args
const { setup } = options
const { setup } = options as DefineComponent
// Avoid wrapping if no options api is used
if (!setup && !options.asyncData && !options.head) {
@ -48,7 +48,18 @@ export const defineNuxtComponent: typeof defineComponent =
...options,
setup (props, ctx) {
const nuxtApp = useNuxtApp()
const res = setup ? Promise.resolve(nuxtApp.runWithContext(() => setup(props, ctx))).then(r => r || {}) : {}
let res = {}
if (setup) {
const fn = (): Promise<Record<string, any>> => Promise.resolve(setup(props, ctx)).then((r: any) => r || {})
const nuxtAppCtx = getNuxtAppCtx(nuxtApp._id)
if (import.meta.server) {
res = nuxtAppCtx.callAsync(nuxtApp, fn)
} else {
nuxtAppCtx.set(nuxtApp)
res = fn()
}
}
const promises: Promise<any>[] = []
if (options.asyncData) {

View File

@ -24,7 +24,7 @@ import type { RouteAnnouncer } from '../app/composables/route-announcer'
// @ts-expect-error virtual file
import { appId, chunkErrorEvent, multiApp } from '#build/nuxt.config.mjs'
function getNuxtAppCtx (id = appId || 'nuxt-app') {
export function getNuxtAppCtx (id = appId || 'nuxt-app') {
return getContext<NuxtApp>(id, {
asyncContext: !!__NUXT_ASYNC_CONTEXT__ && import.meta.server,
})

View File

@ -2862,14 +2862,28 @@ describe('lazy import components', () => {
})
})
describe('defineNuxtComponent watch duplicate', () => {
it('test after navigation duplicate', async () => {
describe('defineNuxtComponent', () => {
it('watches duplicate updates after navigation', async () => {
const { page } = await renderPage('/define-nuxt-component')
await page.getByTestId('define-nuxt-component-bar').click()
await page.getByTestId('define-nuxt-component-state').click()
await page.getByTestId('define-nuxt-component-foo').click()
expect(await page.getByTestId('define-nuxt-component-state').first().innerText()).toBe('2')
})
it('get correctly route when navigating between routes', async () => {
const { page } = await renderPage('/define-nuxt-component/route-1')
await page.getByText('Go to route 2').click()
expect(await page.getByTestId('define-nuxt-component-route-2-path').innerText()).include('route-2')
await page.getByText('Go to route 1').click()
expect(await page.getByTestId('define-nuxt-component-route-1-path').innerText()).include('route-1')
})
it ('should get correctly inject value', async () => {
const { page } = await renderPage('/define-nuxt-component/inject')
expect(await page.getByTestId('define-nuxt-component-inject-value').innerText()).include('bar')
})
})
describe('namespace access to useNuxtApp', () => {

View File

@ -0,0 +1,7 @@
<script setup lang="ts">
provide('foo', 'bar')
</script>
<template>
<NuxtPage />
</template>

View File

@ -0,0 +1,17 @@
<script lang="ts">
export default defineNuxtComponent({
name: 'DefineNuxtComponentTest',
setup () {
const value = inject('foo')
return {
value,
}
},
})
</script>
<template>
<div data-testid="define-nuxt-component-inject-value">
{{ value }}
</div>
</template>

View File

@ -0,0 +1,23 @@
<script lang="ts">
export default defineNuxtComponent({
name: 'DefineNuxtComponentTest',
setup () {
const route = useRoute()
const path = route.path.toString()
return {
path,
}
},
})
</script>
<template>
<div>
<h1>route-1</h1>
<NuxtLink to="/define-nuxt-component/route-2">Go to route 2</NuxtLink>
<div data-testid="define-nuxt-component-route-1-path">
{{ path }}
</div>
</div>
</template>

View File

@ -0,0 +1,23 @@
<script lang="ts">
export default defineNuxtComponent({
name: 'DefineNuxtComponentTest',
setup () {
const route = useRoute()
const path = route.path.toString()
return {
path,
}
},
})
</script>
<template>
<div>
<h2>route-2</h2>
<NuxtLink to="/define-nuxt-component/route-1">Go to route 1</NuxtLink>
<div data-testid="define-nuxt-component-route-2-path">
{{ path }}
</div>
</div>
</template>