feat(nuxt): add onBeforeRouteLeave and onBeforeRouteUpdate composables (#8889)

This commit is contained in:
Daniel Roe 2022-11-10 14:52:04 +01:00 committed by GitHub
parent 3e34a61506
commit e6351349c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 42 additions and 8 deletions

View File

@ -10,7 +10,7 @@ export type { FetchResult, UseFetchOptions } from './fetch'
export { useCookie } from './cookie' export { useCookie } from './cookie'
export type { CookieOptions, CookieRef } from './cookie' export type { CookieOptions, CookieRef } from './cookie'
export { useRequestHeaders, useRequestEvent, setResponseStatus } from './ssr' export { useRequestHeaders, useRequestEvent, setResponseStatus } from './ssr'
export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, setPageLayout, navigateTo, useRoute, useActiveRoute, useRouter } from './router' export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, onBeforeRouteLeave, onBeforeRouteUpdate, setPageLayout, navigateTo, useRoute, useActiveRoute, useRouter } from './router'
export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router' export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router'
export { preloadComponents, prefetchComponents, preloadRouteComponents } from './preload' export { preloadComponents, prefetchComponents, preloadRouteComponents } from './preload'
export { isPrerendered, loadPayload, preloadPayload } from './payload' export { isPrerendered, loadPayload, preloadPayload } from './payload'

View File

@ -1,4 +1,4 @@
import { getCurrentInstance, inject } from 'vue' import { getCurrentInstance, inject, onUnmounted } from 'vue'
import type { Router, RouteLocationNormalizedLoaded, NavigationGuard, RouteLocationNormalized, RouteLocationRaw, NavigationFailure, RouteLocationPathRaw } from 'vue-router' import type { Router, RouteLocationNormalizedLoaded, NavigationGuard, RouteLocationNormalized, RouteLocationRaw, NavigationFailure, RouteLocationPathRaw } from 'vue-router'
import { sendRedirect } from 'h3' import { sendRedirect } from 'h3'
import { hasProtocol, joinURL, parseURL } from 'ufo' import { hasProtocol, joinURL, parseURL } from 'ufo'
@ -17,6 +17,19 @@ export const useRoute = (): RouteLocationNormalizedLoaded => {
return useNuxtApp()._route return useNuxtApp()._route
} }
export const onBeforeRouteLeave = (guard: NavigationGuard) => {
const unsubscribe = useRouter().beforeEach((to, from, next) => {
if (to === from) { return }
return guard(to, from, next)
})
onUnmounted(unsubscribe)
}
export const onBeforeRouteUpdate = (guard: NavigationGuard) => {
const unsubscribe = useRouter().beforeEach(guard)
onUnmounted(unsubscribe)
}
/** @deprecated Use `useRoute` instead. */ /** @deprecated Use `useRoute` instead. */
export const useActiveRoute = (): RouteLocationNormalizedLoaded => { export const useActiveRoute = (): RouteLocationNormalizedLoaded => {
return useNuxtApp()._route return useNuxtApp()._route

View File

@ -31,23 +31,26 @@ export default defineNuxtModule<Partial<ImportsOptions>>({
options = defu(nuxt.options.autoImports, options) options = defu(nuxt.options.autoImports, options)
} }
// Allow modules extending sources // TODO: fix sharing of defaults between invocations of modules
await nuxt.callHook('imports:sources', options.presets as ImportPresetWithDeprecation[]) const presets = JSON.parse(JSON.stringify(options.presets)) as ImportPresetWithDeprecation[]
options.presets?.forEach((_i) => { // Allow modules extending sources
await nuxt.callHook('imports:sources', presets)
for (const _i of presets) {
const i = _i as ImportPresetWithDeprecation | string const i = _i as ImportPresetWithDeprecation | string
if (typeof i !== 'string' && i.names && !i.imports) { if (typeof i !== 'string' && i.names && !i.imports) {
i.imports = i.names i.imports = i.names
logger.warn('imports: presets.names is deprecated, use presets.imports instead') logger.warn('imports: presets.names is deprecated, use presets.imports instead')
} }
}) }
// Filter disabled sources // Filter disabled sources
// options.sources = options.sources.filter(source => source.disabled !== true) // options.sources = options.sources.filter(source => source.disabled !== true)
// Create a context to share state between module internals // Create a context to share state between module internals
const ctx = createUnimport({ const ctx = createUnimport({
presets: options.presets, presets,
imports: options.imports, imports: options.imports,
virtualImports: ['#imports'], virtualImports: ['#imports'],
addons: { addons: {

View File

@ -64,6 +64,15 @@ const appPreset = defineUnimportPreset({
] ]
}) })
// vue-router
const routerPreset = defineUnimportPreset({
from: '#app',
imports: [
'onBeforeRouteLeave',
'onBeforeRouteUpdate'
]
})
// vue // vue
const vuePreset = defineUnimportPreset({ const vuePreset = defineUnimportPreset({
from: 'vue', from: 'vue',
@ -140,5 +149,6 @@ const vuePreset = defineUnimportPreset({
export const defaultPresets: InlinePreset[] = [ export const defaultPresets: InlinePreset[] = [
...commonPresets, ...commonPresets,
appPreset, appPreset,
routerPreset,
vuePreset vuePreset
] ]

View File

@ -54,6 +54,14 @@ export default defineNuxtModule({
references.push({ types: 'vue-router' }) references.push({ types: 'vue-router' })
}) })
// Add vue-router route guard imports
nuxt.hook('imports:sources', (sources) => {
const routerImports = sources.find(s => s.from === '#app' && s.imports.includes('onBeforeRouteLeave'))
if (routerImports) {
routerImports.from = 'vue-router'
}
})
// Regenerate templates when adding or removing pages // Regenerate templates when adding or removing pages
nuxt.hook('builder:watch', async (event, path) => { nuxt.hook('builder:watch', async (event, path) => {
const dirs = [ const dirs = [

View File

@ -66,7 +66,7 @@ describe('imports:nuxt', () => {
continue continue
} }
it(`should register ${name} globally`, () => { it(`should register ${name} globally`, () => {
expect(defaultPresets.find(a => a.from === '#app')!.imports).to.include(name) expect(defaultPresets.flatMap(a => a.from === '#app' ? a.imports : [])).to.include(name)
}) })
} }
} catch (e) { } catch (e) {