---
title: "layouts"
description: "Nuxt 提供了布局框架，用于将常见的 UI 模式提取到可复用的布局中。"
canonical_url: "https://nuxt.zhcndoc.com/docs/4.x/directory-structure/app/layouts"
---
# layouts

> Nuxt 提供了布局框架，用于将常见的 UI 模式提取到可复用的布局中。

::tip{icon="i-lucide-rocket"}
为了获得最佳性能，放置在此目录中的组件在被使用时会通过异步导入自动加载。
::

## 启用 布局

将 [`<NuxtLayout>`](https://nuxt.zhcndoc.com/docs/4.x/api/components/nuxt-layout) 添加到 [`app.vue`](https://nuxt.zhcndoc.com/docs/4.x/directory-structure/app/app) 即可启用布局：

```vue [app/app.vue]
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>
```

使用布局的方法：

- 在页面中通过 [definePageMeta](https://nuxt.zhcndoc.com/docs/4.x/api/utils/define-page-meta) 设置 `layout` 属性。
- 设置 `<NuxtLayout>` 的 `name` prop。
- 在路由规则中设置 `appLayout` 属性。

::note
布局名称会被规范化为 kebab-case，因此 `someLayout` 会变为 `some-layout`。
::

::note
如果未指定布局，则会使用 `app/layouts/default.vue`。
::

::important
如果你的应用中只有一个布局，我们建议改用 [`app.vue`](https://nuxt.zhcndoc.com/docs/4.x/directory-structure/app/app)。
::

::important
与其他组件不同，布局必须只有单个根元素，以便 Nuxt 在布局切换时应用过渡 — 并且该根元素不能是 `<slot />`。
::

## 默认 布局

添加一个 `~/layouts/default.vue`：

```vue [app/layouts/default.vue]
<template>
  <div>
    <p>跨所有页面共享的一些默认布局内容</p>
    <slot />
  </div>
</template>
```

在布局文件中，页面的内容会显示在 `<slot />` 组件中。

## 命名 布局

```bash [目录结构]
-| layouts/
---| default.vue
---| custom.vue
```

然后你可以在页面中使用 `custom` 布局：

```vue twoslash [pages/about.vue]
<script setup lang="ts">
declare module 'nuxt/app' {
  interface NuxtLayouts {
    'custom': unknown
  }
}
// ---cut---
definePageMeta({
  layout: 'custom',
})
</script>
```

::read-more{to="https://nuxt.zhcndoc.com/docs/4.x/directory-structure/app/pages#page-metadata"}
详细了解 `definePageMeta`。
::

你可以使用 [`<NuxtLayout>`](https://nuxt.zhcndoc.com/docs/4.x/api/components/nuxt-layout) 的 `name` 属性，直接覆盖所有页面的默认布局：

```vue [app/app.vue]
<script setup lang="ts">
// 你可以根据 API 调用或登录状态选择布局
const layout = 'custom'
</script>

<template>
  <NuxtLayout :name="layout">
    <NuxtPage />
  </NuxtLayout>
</template>
```

如果你的布局位于嵌套目录中，布局的名称将基于其目录路径和文件名生成，重复的段将被移除。

| 文件                                | 布局名称              |
| --------------------------------- | ----------------- |
| `~/layouts/desktop/default.vue`   | `desktop-default` |
| `~/layouts/desktop-base/base.vue` | `desktop-base`    |
| `~/layouts/desktop/index.vue`     | `desktop`         |

为了清晰起见，我们建议布局的文件名与其名称相匹配：

| 文件                                       | 布局名称              |
| ---------------------------------------- | ----------------- |
| `~/layouts/desktop/DesktopDefault.vue`   | `desktop-default` |
| `~/layouts/desktop-base/DesktopBase.vue` | `desktop-base`    |
| `~/layouts/desktop/Desktop.vue`          | `desktop`         |

::link-example{to="https://nuxt.zhcndoc.com/docs/examples/features/layouts"}
::

## 动态更改布局

你也可以使用 [`setPageLayout`](https://nuxt.zhcndoc.com/docs/4.x/api/utils/set-page-layout) 辅助函数动态更改布局：

```vue twoslash [app/pages/index.vue]
<script setup lang="ts">
declare module 'nuxt/app' {
  interface NuxtLayouts {
    'custom': unknown
  }
}
// ---cut---
function enableCustomLayout () {
  setPageLayout('custom')
}
definePageMeta({
  layout: false,
})
</script>

<template>
  <div>
    <button @click="enableCustomLayout">
      更新布局
    </button>
  </div>
</template>
```

你也可以使用路由规则中的 `appLayout` 属性为特定路由设置布局 :badge[v4.3]{color="info" size="xs" .align-middle}：

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  routeRules: {
    // 为特定路由设置布局
    '/admin': { appLayout: 'admin' },
    // 为多个路由设置布局
    '/dashboard/**': { appLayout: 'dashboard' },
    // 为某条路由禁用布局
    '/landing': { appLayout: false },
  },
})
```

::tip
当你想在配置中集中管理布局（而不是在每个页面文件中分别处理），或者你需要将布局应用到没有对应页面组件的路由（例如可能匹配许多路径的 catchall 页面）时，这非常有用。
::

::link-example{to="https://nuxt.zhcndoc.com/docs/examples/features/layouts"}
::

## 向布局传递 Props :badge[+4.4]{color="primary" .align-middle}

你可以通过多种方式向布局传递 props。

### 通过 `definePageMeta`

对 `layout` 属性使用对象语法，可直接从页面将 props 传递给布局：

::code-group
```vue [app/pages/dashboard.vue]
<script setup lang="ts">
definePageMeta({
  layout: {
    name: 'panel',
    props: {
      sidebar: true,
      title: 'Dashboard',
    },
  },
})
</script>
```

```vue [app/layouts/panel.vue]
<script setup lang="ts">
const props = defineProps<{
  sidebar?: boolean
  title?: string
}>()
</script>

<template>
  <div>
    <aside v-if="sidebar">
      侧边栏
    </aside>
    <main>
      <h1>{{ title }}</h1>
      <slot />
    </main>
  </div>
</template>
```
::

::tip
props 会基于你布局中的 `defineProps` 完全类型化。你将在编辑器中获得自动补全和类型检查。
::

### 通过 `setPageLayout`

你也可以在使用 [`setPageLayout`](https://nuxt.zhcndoc.com/docs/4.x/api/utils/set-page-layout) 动态更改布局时传递 props：

```ts
setPageLayout('panel', { sidebar: true, title: 'Dashboard' })
```

## 在每个页面级别覆盖布局

如果你使用页面（pages），可以通过设置 `layout: false` 并在页面内使用 `<NuxtLayout>` 组件来完全控制布局。

::code-group
```vue [app/pages/index.vue]
<script setup lang="ts">
definePageMeta({
  layout: false,
})
</script>

<template>
  <div>
    <NuxtLayout name="custom">
      <template #header>
        一些头部模板内容。
      </template>

      页面其余内容
    </NuxtLayout>
  </div>
</template>
```

```vue [app/layouts/custom.vue]
<template>
  <div>
    <header>
      <slot name="header">
        默认头部内容
      </slot>
    </header>
    <main>
      <slot />
    </main>
  </div>
</template>
```
::

::important
如果你在页面中使用 `<NuxtLayout>`，请确保它不是根元素（或者[禁用布局/页面过渡](https://nuxt.zhcndoc.com/docs/4.x/getting-started/transitions#disable-transitions)）。
::


## Sitemap

See the full [sitemap](https://nuxt.zhcndoc.com/sitemap.md) for all pages.
