---
title: "页面和布局"
description: "学习如何从 Nuxt 2 迁移到 Nuxt 3 的页面和布局。"
canonical_url: "https://nuxt.zhcndoc.com/docs/3.x/migration/pages-and-layouts"
---
# 页面和布局

> 学习如何从 Nuxt 2 迁移到 Nuxt 3 的页面和布局。

## `app.vue`

Nuxt 3 通过 `~/app.vue` 为您的应用程序提供了一个中央入口点。

::note
如果您的源目录中没有 `app.vue` 文件，Nuxt 会使用它自己的默认版本。
::

此文件是放置任何需要在应用启动时运行一次的自定义代码以及出现在应用每个页面上的任何组件的绝佳位置。例如，如果您只有一个布局，可以将其移到 `app.vue` 中。

::read-more{to="https://nuxt.zhcndoc.com/docs/3.x/directory-structure/app"}
::

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

### 迁移

考虑创建一个 `app.vue` 文件，并在其中包含需要在应用顶层运行一次的任何逻辑。您可以查看[此处的示例](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/app)。

## 布局

如果您的应用为多个页面使用布局，所需更改非常少。

在 Nuxt 2 中，`<Nuxt>` 组件用于在布局中渲染当前页面。在 Nuxt 3 中，布局改用插槽，因此您需要将该组件替换为 `<slot />`。这也支持使用具名插槽和作用域插槽的高级场景。[了解有关布局的更多信息](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/layouts)。

您还需要更改页面中使用布局的定义方式，使用 `definePageMeta` 编译宏设置布局。布局名称将采用短横线命名法（kebab-case）。例如，`layouts/customLayout.vue` 在页面中引用时变为 `custom-layout`。

### 迁移

1. 将 `<Nuxt />` 替换为 `<slot />`
   ```diff [layouts/custom.vue]
     <template>
       <div id="app-layout">
         <main>
   -       <Nuxt />
   +       <slot />
         </main>
       </div>
     </template>
   ```
2. 使用 [`definePageMeta`](https://nuxt.zhcndoc.com/docs/3.x/api/utils/define-page-meta) 选择页面使用的布局。
   ```diff [pages/index.vue]
   + <script setup>
   + definePageMeta({
   +   layout: 'custom'
   + })
   - <script>
   - export default {
   -   layout: 'custom'
   - }
     </script>
   ```
3. 将 `~/layouts/_error.vue` 移至 `~/error.vue`。请参阅[错误处理文档](https://nuxt.zhcndoc.com/docs/3.x/getting-started/error-handling)。如果您想确保此页面使用布局，可以在 `error.vue` 中直接使用 [`<NuxtLayout>`](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/layouts)：
   ```vue [error.vue]
   <template>
     <div>
       <NuxtLayout name="default">
         <!-- -->
       </NuxtLayout>
     </div>
   </template>
   ```

## 页面

Nuxt 3 提供可选的 `vue-router` 集成，当源目录中存在 [`pages/`](https://nuxt.zhcndoc.com/docs/3.x/directory-structure/pages) 目录时会触发。如果您只有一个页面，可以考虑将其移至 `app.vue`，以构建更轻量的应用。

### 动态路由

Nuxt 3 定义动态路由的格式与 Nuxt 2 略有不同，因此您可能需要重命名 `pages/` 目录中的某些文件。

1. 之前使用 `_id` 定义动态路由参数，现在改为使用 `[id]`。
2. 之前使用 `_.vue` 定义通配路由，现在改为 `[...slug].vue`。

### 嵌套路由

在 Nuxt 2 中，您会使用 `<Nuxt>` 和 `<NuxtChild>` 定义父子组件的嵌套路由。Nuxt 3 中，这些已被单个 `<NuxtPage>` 组件替代。

### 页面键和 Keep-alive 属性

如果您向 `<Nuxt>` 传递自定义页面键或 keep-alive 属性，现在需使用 `definePageMeta` 设置这些选项。

::read-more{to="https://nuxt.zhcndoc.com/docs/3.x/directory-structure/pages#special-metadata"}
::

### 页面与布局切换动画

如果您曾在组件选项中直接定义页面或布局的过渡动画，现在需使用 `definePageMeta` 设置该过渡。自 Vue 3 起，[-enter 和 -leave CSS 类名已重命名](https://v3-migration.vuejs.org/breaking-changes/transition.html)。在 `<slot>` 上使用 `<Nuxt>` 的 `style` 属性不再应用于过渡，请将样式移至 `-active` 类。

::read-more{to="https://nuxt.zhcndoc.com/docs/3.x/getting-started/transitions"}
::

### 迁移

1. 将所有带有动态参数的页面重命名为符合新格式的名称。
2. 将 `<Nuxt>` 和 `<NuxtChild>` 更新为 `<NuxtPage>`。
3. 如果您使用 Composition API，也可以将 `this.$route` 和 `this.$router` 迁移为使用 [`useRoute`](https://nuxt.zhcndoc.com/docs/3.x/api/composables/use-route) 和 [`useRouter`](https://nuxt.zhcndoc.com/docs/3.x/api/composables/use-router) 组合式函数。

#### 示例：动态路由

::code-group
``` [Nuxt 2]
- URL: /users
- Page: /pages/users/index.vue

- URL: /users/some-user-name
- Page: /pages/users/_user.vue
- Usage: params.user

- URL: /users/some-user-name/edit
- Page: /pages/users/_user/edit.vue
- Usage: params.user

- URL: /users/anything-else
- Page: /pages/users/_.vue
- Usage: params.pathMatch
```

``` [Nuxt 3]
- URL: /users
- Page: /pages/users/index.vue

- URL: /users/some-user-name
- Page: /pages/users/[user].vue
- Usage: params.user

- URL: /users/some-user-name/edit
- Page: /pages/users/[user]/edit.vue
- Usage: params.user

- URL: /users/anything-else
- Page: /pages/users/[...slug].vue
- Usage: params.slug
```
::

#### 示例：嵌套路由和 `definePageMeta`

::code-group
```vue [Nuxt 2]
<template>
  <div>
    <NuxtChild
      keep-alive
      :keep-alive-props="{ exclude: ['modal'] }"
      :nuxt-child-key="$route.slug"
    />
  </div>
</template>

<script>
export default {
  transition: 'page', // or { name: 'page' }
}
</script>
```

```vue [Nuxt 3]
<template>
  <div>
    <NuxtPage />
  </div>
</template>

<script setup lang="ts">
// 此编译宏适用于 <script> 和 <script setup>
definePageMeta({
  // 你也可以传入字符串或计算属性
  key: route => route.slug,
  transition: {
    name: 'page',
  },
  keepalive: {
    exclude: ['modal'],
  },
})
</script>
```
::

## `<NuxtLink>` 组件

全局 [NuxtLink](https://nuxt.zhcndoc.com/docs/3.x/api/components/nuxt-link) 组件的大部分语法和功能都相同。如果您一直使用简写形式 `<NLink>`，则应将其更新为使用 `<NuxtLink>`。

`<NuxtLink>` 现在可作为所有链接的免替换组件，即使是外部链接。您可以阅读更多关于它的信息，以及如何扩展以提供您自己的链接组件。

::read-more{to="https://nuxt.zhcndoc.com/docs/3.x/api/components/nuxt-link"}
::

## 编程式导航

从 Nuxt 2 迁移到 Nuxt 3 时，您需要更新编程式导航的方式。Nuxt 2 中，您可以通过 `this.$router` 访问底层 Vue Router。Nuxt 3 中，您可以使用 `navigateTo()` 工具方法，将路由和参数传递给 Vue Router。

::warning
请务必始终对 [`navigateTo`](https://nuxt.zhcndoc.com/docs/3.x/api/utils/navigate-to) 使用 `await`，或通过从函数中返回其结果来链接调用。
::

::code-group
```vue [Nuxt 2]
<script>
export default {
  methods: {
    navigate () {
      this.$router.push({
        path: '/search',
        query: {
          name: 'first name',
          type: '1',
        },
      })
    },
  },
}
</script>
```

```vue [Nuxt 3]
<script setup lang="ts">
function navigate () {
  return navigateTo({
    path: '/search',
    query: {
      name: 'first name',
      type: '1',
    },
  })
}
</script>
```
::


## Sitemap

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