---
title: "页面和布局"
description: "学习如何将 Nuxt 2 的页面和布局迁移到 Nuxt 3。"
canonical_url: "https://nuxt.zhcndoc.com/docs/4.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/4.x/directory-structure/app/app"}
::

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

### 迁移

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

## 布局

如果你的应用在多个页面中使用布局，仅需做很小的更改。

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

你还需要更改在页面中定义要使用的布局的方式，使用 `definePageMeta` 编译宏。布局名称将转换为 kebab-case。因此 `app/layouts/customLayout.vue` 在页面中引用时将变成 `custom-layout`。

### 迁移

1. 将 `<Nuxt />` 替换为 `<slot />`
   ```diff [app/layouts/custom.vue]
     <template>
       <div id="app-layout">
         <main>
   -       <Nuxt />
   +       <slot />
         </main>
       </div>
     </template>
   ```
2. 使用 [`definePageMeta`](https://nuxt.zhcndoc.com/docs/4.x/api/utils/define-page-meta) 选择页面使用的布局。
   ```diff [app/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/4.x/getting-started/error-handling)。如果你想确保此页面使用布局，可以直接在 `error.vue` 中使用 [`<NuxtLayout>`](https://nuxt.zhcndoc.com/docs/4.x/directory-structure/app/layouts)：
   ```vue [error.vue]
   <template>
     <div>
       <NuxtLayout name="default">
         <!-- -->
       </NuxtLayout>
     </div>
   </template>
   ```

## 页面

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

### 动态路由

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

1. 之前使用 `_id` 来定义动态路由参数，现在使用 `[id]`。
2. 之前使用 `_.vue` 来定义 catch-all 路由，现在使用 `[...slug].vue`。

### 嵌套路由

在 Nuxt 2 中，你会使用 `<Nuxt>` 和 `<NuxtChild>` 来定义任何嵌套路由（带父组件和子组件）。在 Nuxt 3 中，它们已被单个 `<NuxtPage>` 组件取代。

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

如果你之前把自定义页面键或 keep-alive 属性传递给 `<Nuxt>`，现在需要使用 `definePageMeta` 来设置这些选项。

::read-more{to="https://nuxt.zhcndoc.com/docs/4.x/directory-structure/app/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/4.x/getting-started/transitions"}
::

### 迁移

1. 将所有带有动态参数的页面重命名为新格式。
2. 将 `<Nuxt>` 和 `<NuxtChild>` 更新为 `<NuxtPage>`。
3. 如果你正在使用 Composition API，也可以将 `this.$route` 和 `this.$router` 迁移为使用 [`useRoute`](https://nuxt.zhcndoc.com/docs/4.x/api/composables/use-route) 和 [`useRouter`](https://nuxt.zhcndoc.com/docs/4.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">
// This compiler macro works in both <script> and <script setup>
definePageMeta({
  // you can also pass a string or a computed property
  key: route => route.slug,
  transition: {
    name: 'page',
  },
  keepalive: {
    exclude: ['modal'],
  },
})
</script>
```
::

## `<NuxtLink>` 组件

全局 [NuxtLink](https://nuxt.zhcndoc.com/docs/4.x/api/components/nuxt-link) 组件的大多数语法和功能都相同。如果你一直使用快捷格式 `<NLink>`，应该将其更新为 `<NuxtLink>`。

`<NuxtLink>` 现在可以作为所有链接的替代项，即使是外部链接。你可以阅读更多内容，了解如何扩展它以提供自定义的链接组件。

::read-more{to="https://nuxt.zhcndoc.com/docs/4.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/4.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.
