fix(nuxt): prioritise vue app context when available (#20910)

This commit is contained in:
Daniel Roe 2023-05-17 13:26:16 +01:00 committed by GitHub
parent f39eb6e981
commit d2e14b678b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 11 deletions

View File

@ -1,5 +1,5 @@
/* eslint-disable no-use-before-define */
import { getCurrentInstance, reactive } from 'vue'
import { getCurrentInstance, hasInjectionContext, reactive } from 'vue'
import type { App, Ref, VNode, onErrorCaptured } from 'vue'
import type { RouteLocationNormalizedLoaded } from '#vue-router'
import type { HookCallback, Hookable } from 'hookable'
@ -435,19 +435,20 @@ export function callWithNuxt<T extends (...args: any[]) => any> (nuxt: NuxtApp |
/**
* Returns the current Nuxt instance.
*/
export function useNuxtApp () {
const nuxtAppInstance = nuxtAppCtx.tryUse()
export function useNuxtApp (): NuxtApp {
let nuxtAppInstance
if (hasInjectionContext()) {
nuxtAppInstance = getCurrentInstance()?.appContext.app.$nuxt
}
nuxtAppInstance = nuxtAppInstance || nuxtAppCtx.tryUse()
if (!nuxtAppInstance) {
const vm = getCurrentInstance()
if (!vm) {
if (process.dev) {
throw new Error('[nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function. This is probably not a Nuxt bug. Find out more at `https://nuxt.com/docs/guide/concepts/auto-imports#using-vue-and-nuxt-composables`.')
} else {
throw new Error('[nuxt] instance unavailable')
}
if (process.dev) {
throw new Error('[nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function. This is probably not a Nuxt bug. Find out more at `https://nuxt.com/docs/guide/concepts/auto-imports#using-vue-and-nuxt-composables`.')
} else {
throw new Error('[nuxt] instance unavailable')
}
return vm.appContext.app.$nuxt as NuxtApp
}
return nuxtAppInstance

View File

@ -596,6 +596,22 @@ describe('navigate', () => {
})
})
describe('preserves current instance', () => {
// TODO: reenable when https://github.com/vuejs/core/issues/7733 is resolved
it('should not return getCurrentInstance when there\'s an error in data', async () => {
await fetch('/instance/error')
const html = await $fetch('/instance/next-request')
expect(html).toContain('This should be false: false')
})
// TODO: re-enable when https://github.com/nuxt/nuxt/issues/15164 is resolved
it.skipIf(isWindows)('should not lose current nuxt app after await in vue component', async () => {
const requests = await Promise.all(Array.from({ length: 100 }).map(() => $fetch('/instance/next-request')))
for (const html of requests) {
expect(html).toContain('This should be true: true')
}
})
})
describe('errors', () => {
it('should render a JSON error page', async () => {
const res = await fetch('/error', {

View File

@ -0,0 +1,13 @@
<script lang="ts">
export default defineComponent({
data () {
throw new Error('💀')
}
})
</script>
<template>
<div>
This should not display.
</div>
</template>

View File

@ -0,0 +1,14 @@
<script setup>
const nuxtApp = useNuxtApp()
await new Promise(resolve => setTimeout(resolve, 150))
const isSameApp = nuxtApp === useNuxtApp()
if (!isSameApp) {
throw new Error('💀')
}
</script>
<template>
<div>
This should be false: {{ $wasVueAppInstanceWronglyPreserved }}
This should be true: {{ isSameApp }}
</div>
</template>

View File

@ -0,0 +1,9 @@
export default defineNuxtPlugin(() => {
// this should be undefined
const vueApp = getCurrentInstance()
return {
provide: {
wasVueAppInstanceWronglyPreserved: !!vueApp
}
}
})