mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 15:15:19 +00:00
Co-authored-by: HomWang <516310460@qq.com>
This commit is contained in:
parent
c92b6a01bb
commit
b90d286631
@ -121,9 +121,8 @@ You can also use a ref or computed property for your layout.
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const route = useRoute()
|
||||
function enableCustomLayout () {
|
||||
route.meta.layout = "custom"
|
||||
setPageLayout('custom')
|
||||
}
|
||||
definePageMeta({
|
||||
layout: false,
|
||||
|
14
docs/content/3.api/1.composables/set-layout.md
Normal file
14
docs/content/3.api/1.composables/set-layout.md
Normal file
@ -0,0 +1,14 @@
|
||||
# `setPageLayout`
|
||||
|
||||
`setPageLayout` allows you to dynamically change the layout of a page. It relies on access to the Nuxt context and can only be called within components' setup functions, plugins, and route middleware.
|
||||
|
||||
```ts
|
||||
export default defineNuxtRouteMiddleware((to) => {
|
||||
// Set the layout on the route you are navigating _to_
|
||||
setPageLayout('other')
|
||||
})
|
||||
```
|
||||
|
||||
::alert{icon=👉}
|
||||
If you choose to set the layout dynamically on the server side, you _must_ do so before the layout is rendered by Vue (that is, within a plugin or route middleware) to avoid a hydration mismatch.
|
||||
::
|
4
examples/routing/layouts/middleware/other.ts
Normal file
4
examples/routing/layouts/middleware/other.ts
Normal file
@ -0,0 +1,4 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export default defineNuxtRouteMiddleware(() => {
|
||||
setPageLayout('other')
|
||||
})
|
@ -11,6 +11,18 @@
|
||||
<NuxtLink to="/dynamic">
|
||||
Dynamic layout
|
||||
</NuxtLink>
|
||||
<NuxtLink to="/other">
|
||||
Other layout
|
||||
</NuxtLink>
|
||||
<NButton @click="setPageLayout('default')">
|
||||
Change to default layout
|
||||
</NButton>
|
||||
<NButton @click="setPageLayout('custom')">
|
||||
Change to custom layout
|
||||
</NButton>
|
||||
<NButton @click="setPageLayout('other')">
|
||||
Change to other layout
|
||||
</NButton>
|
||||
</nav>
|
||||
</template>
|
||||
</NuxtExampleLayout>
|
||||
|
13
examples/routing/layouts/pages/other.vue
Normal file
13
examples/routing/layouts/pages/other.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: 'other'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<NuxtLink to="/">
|
||||
Back to home
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
@ -10,6 +10,6 @@ export type { FetchResult, UseFetchOptions } from './fetch'
|
||||
export { useCookie } from './cookie'
|
||||
export type { CookieOptions, CookieRef } from './cookie'
|
||||
export { useRequestHeaders, useRequestEvent, setResponseStatus } from './ssr'
|
||||
export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, navigateTo, useRoute, useActiveRoute, useRouter } from './router'
|
||||
export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, setPageLayout, navigateTo, useRoute, useActiveRoute, useRouter } from './router'
|
||||
export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router'
|
||||
export { preloadComponents, prefetchComponents } from './preload'
|
||||
|
@ -2,7 +2,7 @@ import { getCurrentInstance, inject } from 'vue'
|
||||
import type { Router, RouteLocationNormalizedLoaded, NavigationGuard, RouteLocationNormalized, RouteLocationRaw, NavigationFailure, RouteLocationPathRaw } from 'vue-router'
|
||||
import { sendRedirect } from 'h3'
|
||||
import { hasProtocol, joinURL, parseURL } from 'ufo'
|
||||
import { useNuxtApp, useRuntimeConfig } from '#app'
|
||||
import { useNuxtApp, useRuntimeConfig, useState } from '#app'
|
||||
|
||||
export const useRouter = () => {
|
||||
return useNuxtApp()?.$router as Router
|
||||
@ -114,3 +114,20 @@ export const abortNavigation = (err?: Error | string) => {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export const setPageLayout = (layout: string) => {
|
||||
if (process.server) {
|
||||
useState('_layout').value = layout
|
||||
}
|
||||
const nuxtApp = useNuxtApp()
|
||||
const inMiddleware = isProcessingMiddleware()
|
||||
if (inMiddleware || process.server || nuxtApp.isHydrating) {
|
||||
const unsubscribe = useRouter().beforeResolve((to) => {
|
||||
to.meta.layout = layout
|
||||
unsubscribe()
|
||||
})
|
||||
}
|
||||
if (!inMiddleware) {
|
||||
useRoute().meta.layout = layout
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { reactive, h } from 'vue'
|
||||
import { parseURL, stringifyParsedURL, parseQuery, stringifyQuery, withoutBase, isEqual, joinURL } from 'ufo'
|
||||
import { createError } from 'h3'
|
||||
import { defineNuxtPlugin, clearError, navigateTo, showError, useRuntimeConfig } from '..'
|
||||
import { defineNuxtPlugin, clearError, navigateTo, showError, useRuntimeConfig, useState } from '..'
|
||||
import { callWithNuxt } from '../nuxt'
|
||||
// @ts-ignore
|
||||
import { globalMiddleware } from '#build/middleware'
|
||||
@ -218,9 +218,13 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
|
||||
named: {}
|
||||
}
|
||||
|
||||
const initialLayout = useState('_layout')
|
||||
nuxtApp.hooks.hookOnce('app:created', async () => {
|
||||
router.beforeEach(async (to, from) => {
|
||||
to.meta = reactive(to.meta || {})
|
||||
if (nuxtApp.isHydrating) {
|
||||
to.meta.layout = initialLayout.value ?? to.meta.layout
|
||||
}
|
||||
nuxtApp._processingMiddleware = true
|
||||
|
||||
const middlewareEntries = new Set<RouteGuard>([...globalMiddleware, ...nuxtApp._middleware.global])
|
||||
|
@ -36,6 +36,7 @@ const appPreset = defineUnimportPreset({
|
||||
'useRequestHeaders',
|
||||
'useRequestEvent',
|
||||
'setResponseStatus',
|
||||
'setPageLayout',
|
||||
'useRouter',
|
||||
'useRoute',
|
||||
'useActiveRoute',
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
import { createError } from 'h3'
|
||||
import { withoutBase, isEqual } from 'ufo'
|
||||
import NuxtPage from './page'
|
||||
import { callWithNuxt, defineNuxtPlugin, useRuntimeConfig, showError, clearError, navigateTo, useError } from '#app'
|
||||
import { callWithNuxt, defineNuxtPlugin, useRuntimeConfig, showError, clearError, navigateTo, useError, useState } from '#app'
|
||||
// @ts-ignore
|
||||
import routes from '#build/routes'
|
||||
// @ts-ignore
|
||||
@ -114,8 +114,12 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
||||
callWithNuxt(nuxtApp, showError, [error])
|
||||
}
|
||||
|
||||
const initialLayout = useState('_layout')
|
||||
router.beforeEach(async (to, from) => {
|
||||
to.meta = reactive(to.meta)
|
||||
if (nuxtApp.isHydrating) {
|
||||
to.meta.layout = initialLayout.value ?? to.meta.layout
|
||||
}
|
||||
nuxtApp._processingMiddleware = true
|
||||
|
||||
type MiddlewareDef = string | NavigationGuard
|
||||
|
@ -273,6 +273,16 @@ describe('layouts', () => {
|
||||
expect(html).toContain('with-layout.vue')
|
||||
expect(html).toContain('Custom Layout:')
|
||||
})
|
||||
it('should work with a dynamically set layout', async () => {
|
||||
const html = await $fetch('/with-dynamic-layout')
|
||||
|
||||
// Snapshot
|
||||
// expect(html).toMatchInlineSnapshot()
|
||||
|
||||
expect(html).toContain('with-dynamic-layout')
|
||||
expect(html).toContain('Custom Layout:')
|
||||
await expectNoClientErrors('/with-dynamic-layout')
|
||||
})
|
||||
})
|
||||
|
||||
describe('reactivity transform', () => {
|
||||
|
4
test/fixtures/basic/middleware/sets-layout.ts
vendored
Normal file
4
test/fixtures/basic/middleware/sets-layout.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
export default defineNuxtRouteMiddleware(async () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 10))
|
||||
setPageLayout('custom')
|
||||
})
|
11
test/fixtures/basic/pages/with-dynamic-layout.vue
vendored
Normal file
11
test/fixtures/basic/pages/with-dynamic-layout.vue
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
middleware: 'sets-layout'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>with-dynamic-layout.vue</div>
|
||||
</div>
|
||||
</template>
|
Loading…
Reference in New Issue
Block a user