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>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const route = useRoute()
|
|
||||||
function enableCustomLayout () {
|
function enableCustomLayout () {
|
||||||
route.meta.layout = "custom"
|
setPageLayout('custom')
|
||||||
}
|
}
|
||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: false,
|
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">
|
<NuxtLink to="/dynamic">
|
||||||
Dynamic layout
|
Dynamic layout
|
||||||
</NuxtLink>
|
</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>
|
</nav>
|
||||||
</template>
|
</template>
|
||||||
</NuxtExampleLayout>
|
</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 { 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, navigateTo, useRoute, useActiveRoute, useRouter } from './router'
|
export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, setPageLayout, navigateTo, useRoute, useActiveRoute, useRouter } from './router'
|
||||||
export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router'
|
export type { AddRouteMiddlewareOptions, RouteMiddleware } from './router'
|
||||||
export { preloadComponents, prefetchComponents } from './preload'
|
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 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'
|
||||||
import { useNuxtApp, useRuntimeConfig } from '#app'
|
import { useNuxtApp, useRuntimeConfig, useState } from '#app'
|
||||||
|
|
||||||
export const useRouter = () => {
|
export const useRouter = () => {
|
||||||
return useNuxtApp()?.$router as Router
|
return useNuxtApp()?.$router as Router
|
||||||
@ -114,3 +114,20 @@ export const abortNavigation = (err?: Error | string) => {
|
|||||||
}
|
}
|
||||||
return false
|
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 { reactive, h } from 'vue'
|
||||||
import { parseURL, stringifyParsedURL, parseQuery, stringifyQuery, withoutBase, isEqual, joinURL } from 'ufo'
|
import { parseURL, stringifyParsedURL, parseQuery, stringifyQuery, withoutBase, isEqual, joinURL } from 'ufo'
|
||||||
import { createError } from 'h3'
|
import { createError } from 'h3'
|
||||||
import { defineNuxtPlugin, clearError, navigateTo, showError, useRuntimeConfig } from '..'
|
import { defineNuxtPlugin, clearError, navigateTo, showError, useRuntimeConfig, useState } from '..'
|
||||||
import { callWithNuxt } from '../nuxt'
|
import { callWithNuxt } from '../nuxt'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { globalMiddleware } from '#build/middleware'
|
import { globalMiddleware } from '#build/middleware'
|
||||||
@ -218,9 +218,13 @@ export default defineNuxtPlugin<{ route: Route, router: Router }>((nuxtApp) => {
|
|||||||
named: {}
|
named: {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initialLayout = useState('_layout')
|
||||||
nuxtApp.hooks.hookOnce('app:created', async () => {
|
nuxtApp.hooks.hookOnce('app:created', async () => {
|
||||||
router.beforeEach(async (to, from) => {
|
router.beforeEach(async (to, from) => {
|
||||||
to.meta = reactive(to.meta || {})
|
to.meta = reactive(to.meta || {})
|
||||||
|
if (nuxtApp.isHydrating) {
|
||||||
|
to.meta.layout = initialLayout.value ?? to.meta.layout
|
||||||
|
}
|
||||||
nuxtApp._processingMiddleware = true
|
nuxtApp._processingMiddleware = true
|
||||||
|
|
||||||
const middlewareEntries = new Set<RouteGuard>([...globalMiddleware, ...nuxtApp._middleware.global])
|
const middlewareEntries = new Set<RouteGuard>([...globalMiddleware, ...nuxtApp._middleware.global])
|
||||||
|
@ -36,6 +36,7 @@ const appPreset = defineUnimportPreset({
|
|||||||
'useRequestHeaders',
|
'useRequestHeaders',
|
||||||
'useRequestEvent',
|
'useRequestEvent',
|
||||||
'setResponseStatus',
|
'setResponseStatus',
|
||||||
|
'setPageLayout',
|
||||||
'useRouter',
|
'useRouter',
|
||||||
'useRoute',
|
'useRoute',
|
||||||
'useActiveRoute',
|
'useActiveRoute',
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
import { createError } from 'h3'
|
import { createError } from 'h3'
|
||||||
import { withoutBase, isEqual } from 'ufo'
|
import { withoutBase, isEqual } from 'ufo'
|
||||||
import NuxtPage from './page'
|
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
|
// @ts-ignore
|
||||||
import routes from '#build/routes'
|
import routes from '#build/routes'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -114,8 +114,12 @@ export default defineNuxtPlugin(async (nuxtApp) => {
|
|||||||
callWithNuxt(nuxtApp, showError, [error])
|
callWithNuxt(nuxtApp, showError, [error])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initialLayout = useState('_layout')
|
||||||
router.beforeEach(async (to, from) => {
|
router.beforeEach(async (to, from) => {
|
||||||
to.meta = reactive(to.meta)
|
to.meta = reactive(to.meta)
|
||||||
|
if (nuxtApp.isHydrating) {
|
||||||
|
to.meta.layout = initialLayout.value ?? to.meta.layout
|
||||||
|
}
|
||||||
nuxtApp._processingMiddleware = true
|
nuxtApp._processingMiddleware = true
|
||||||
|
|
||||||
type MiddlewareDef = string | NavigationGuard
|
type MiddlewareDef = string | NavigationGuard
|
||||||
|
@ -273,6 +273,16 @@ describe('layouts', () => {
|
|||||||
expect(html).toContain('with-layout.vue')
|
expect(html).toContain('with-layout.vue')
|
||||||
expect(html).toContain('Custom Layout:')
|
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', () => {
|
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