Nuxt Nation 大会即将到来。加入我们,时间为 11 月 12 日至 13 日。

升级指南

了解如何升级到最新的 Nuxt 版本。

升级 Nuxt

最新版本

要将 Nuxt 升级到最新版本,请使用nuxi upgrade命令。

npx nuxi upgrade

Nightly 发布渠道

要使用最新的 Nuxt 构建和测试功能(在发布之前),请阅读有关Nightly 发布渠道指南。

Nightly 发布渠道latest标签目前跟踪 Nuxt v4 分支,这意味着它现在特别可能存在重大更改 - 请注意!您可以使用"nuxt": "npm:nuxt-nightly@3x"选择加入 3.x 分支的 Nightly 版本。

测试 Nuxt 4

Nuxt 4 的发布日期将另行通知。它取决于在 Nitro 的主要版本发布后是否有足够的时间在社区中进行适当的测试。您可以在此 PR中关注 Nitro 发布的进度。

在发布之前,可以从 Nuxt 版本 3.12+ 测试 Nuxt 4 的许多重大更改。

观看 Alexander Lichter 的视频,了解如何选择加入 Nuxt 4 的重大更改。

选择加入 Nuxt 4

首先,将 Nuxt 升级到最新版本

然后,您可以将compatibilityVersion设置为匹配 Nuxt 4 的行为

nuxt.config.ts
export default 
defineNuxtConfig
({
future
: {
compatibilityVersion
: 4,
}, // To re-enable _all_ Nuxt v3 behavior, set the following options: // srcDir: '.', // dir: { // app: 'app' // }, // experimental: { // scanPageMeta: 'after-resolve', // sharedPrerenderData: false, // compileTemplate: true, // resetAsyncDataToUndefined: true, // templateUtils: true, // relativeWatchPaths: true, // normalizeComponentNames: false // defaults: { // useAsyncData: { // deep: true // } // } // }, // unhead: { // renderSSRHeadOptions: { // omitLineBreaks: false // } // } })

当您将compatibilityVersion设置为4时,Nuxt 配置中的默认值将更改为选择加入 Nuxt v4 的行为,但在测试时,您可以根据上面注释掉的代码行逐一重新启用 Nuxt v3 的行为。如果遇到此类情况,请提交问题,以便我们可以在 Nuxt 或生态系统中解决它们。

迁移到 Nuxt 4

重大或重要的更改将在此处记录,以及用于向后/向前兼容性的迁移步骤。

此部分在最终发布之前可能会发生变化,因此如果您正在使用compatibilityVersion: 4测试 Nuxt 4,请定期查看此处。

使用 Codemods 迁移

为了促进升级过程,我们与Codemod团队合作,使用一些开源 Codemods 自动化许多迁移步骤。

如果您遇到任何问题,请使用npx codemod feedback向 Codemod 团队报告🙏

有关 Nuxt 4 Codemods 的完整列表,每个 Codemods 的详细信息、其源代码以及各种运行方式,请访问Codemod 注册表

您可以使用以下codemod配方运行本指南中提到的所有 Codemods

npx codemod@latest nuxt/4/migration-recipe

此命令将按顺序执行所有 Codemods,并可以选择取消选择您不想运行的任何 Codemods。每个 Codemod 也列在下面,以及其相应的更改,并且可以独立执行。

新的目录结构

🚦 影响级别:重大

Nuxt 现在默认为新的目录结构,并提供向后兼容性(因此,如果 Nuxt 检测到您正在使用旧结构,例如使用顶层pages/目录,则此新结构将不适用)。

👉 查看完整 RFC

更改内容
  • 新的 Nuxt 默认srcDir默认为app/,大多数内容都从那里解析。
  • serverDir现在默认为<rootDir>/server而不是<srcDir>/server
  • layers/modules/public/默认情况下相对于<rootDir>解析
  • 如果使用Nuxt Content v2.13+content/相对于<rootDir>解析
  • 添加了一个新的dir.app,这是我们查找router.options.tsspa-loading-template.html的目录 - 此目录默认为<srcDir>/
一个示例 v4 文件夹结构。
.output/
.nuxt/
app/
  assets/
  components/
  composables/
  layouts/
  middleware/
  pages/
  plugins/
  utils/
  app.config.ts
  app.vue
  router.options.ts
content/
layers/
modules/
node_modules/
public/
server/
  api/
  middleware/
  plugins/
  routes/
  utils/
nuxt.config.ts

👉 有关更多详细信息,请参阅实现此更改的 PR

更改原因
  1. 性能 - 将所有代码放在存储库的根目录会导致.git/node_modules/文件夹被 FS 监视程序扫描/包含,这会大大延迟非 Mac OS 上的启动。
  2. IDE 类型安全性 - server/和应用程序的其余部分在两个完全不同的上下文中运行,具有不同的全局导入可用,并确保server/不在与应用程序其余部分相同的文件夹中是确保在 IDE 中获得良好自动完成功能的重要第一步。
迁移步骤
  1. 创建一个名为app/的新目录。
  2. 将您的assets/components/composables/layouts/middleware/pages/plugins/utils/文件夹以及app.vueerror.vueapp.config.ts移动到该目录下。如果您有app/router-options.tsapp/spa-loading-template.html,则这些路径保持不变。
  3. 确保您的nuxt.config.tscontent/layers/modules/public/server/文件夹保留在app/文件夹之外,位于项目的根目录中。
  4. 请记住更新任何第三方配置文件以与新的目录结构一起使用,例如您的tailwindcsseslint配置(如果需要 - @nuxtjs/tailwindcss应该会自动正确配置tailwindcss)。
您可以通过运行npx codemod@latest nuxt/4/file-structure来自动化此迁移

但是,迁移不是必需的。如果您希望保留当前的文件夹结构,Nuxt 应该会自动检测它。(如果它没有,请提出问题。)唯一的例外是,如果您已经拥有自定义srcDir。在这种情况下,您应该知道您的modules/public/server/文件夹将从您的rootDir而不是从您的自定义srcDir解析。如果需要,您可以通过配置dir.modulesdir.publicserverDir来覆盖此设置。

您还可以使用以下配置强制使用 v3 文件夹结构

nuxt.config.ts
export default defineNuxtConfig({
  // This reverts the new srcDir default from `app` back to your root directory
  srcDir: '.',
  // This specifies the directory prefix for `app/router.options.ts` and `app/spa-loading-template.html`
  dir: {
    app: 'app'
  }
})

路由元数据的去重

🚦 影响级别:最低

更改内容

可以使用definePageMeta设置一些路由元数据,例如namepath等。以前这些元数据在路由和路由元数据中都可用(例如,route.nameroute.meta.name)。

现在,它们只能在路由对象上访问。

更改原因

这是由于默认情况下启用了experimental.scanPageMeta,并且是一种性能优化。

迁移步骤

迁移应该很简单

  const route = useRoute()
  
- console.log(route.meta.name)
+ console.log(route.name)

规范化的组件名称

🚦 影响级别:中等

Vue 现在将生成与 Nuxt 组件命名模式匹配的组件名称。

更改内容

默认情况下,如果您没有手动设置,Vue 将分配一个与组件文件名匹配的组件名称。

目录结构
├─ components/
├─── SomeFolder/
├───── MyComponent.vue

在这种情况下,就 Vue 而言,组件名称将为MyComponent。如果您想使用<KeepAlive>或在 Vue DevTools 中识别它,则需要使用此名称。

但为了自动导入它,您需要使用SomeFolderMyComponent

通过此更改,这两个值将匹配,并且 Vue 将生成与 Nuxt 组件命名模式匹配的组件名称。

迁移步骤

确保在任何使用@vue/test-utils中的findComponent以及任何依赖于组件名称的<KeepAlive>的测试中使用更新后的名称。

或者,目前,您可以使用以下方法禁用此行为

nuxt.config.ts
export default 
defineNuxtConfig
({
experimental
: {
normalizeComponentNames
: false
} })

解析后扫描页面元数据

🚦 影响级别:最低

更改内容

我们现在在调用pages:extend钩子之后扫描页面元数据(在definePageMeta中定义),而不是之前。

更改原因

这是为了允许扫描用户希望在pages:extend中添加的页面的元数据。我们仍然提供了一个机会,可以在新的pages:resolved钩子中更改或覆盖页面元数据。

迁移步骤

如果您想覆盖页面元数据,请在pages:resolved中而不是在pages:extend中执行此操作。

  export default defineNuxtConfig({
    hooks: {
-     'pages:extend'(pages) {
+     'pages:resolved'(pages) {
        const myPage = pages.find(page => page.path === '/')
        myPage.meta ||= {}
        myPage.meta.layout = 'overridden-layout'
      }
    }
  })

或者,您可以使用以下方法恢复到之前的行为

nuxt.config.ts
export default 
defineNuxtConfig
({
experimental
: {
scanPageMeta
: true
} })

共享预渲染数据

🚦 影响级别:中等

更改内容

我们启用了一个之前实验性的功能,用于跨不同页面共享来自 useAsyncDatauseFetch 调用的数据。请参阅 原始 PR

更改原因

此功能会在预渲染的页面之间自动共享有效负载的 data。当预渲染使用 useAsyncDatauseFetch 并获取不同页面中相同数据的站点时,这可以带来显著的性能提升。

例如,如果您的站点每个页面都需要一个 useFetch 调用(例如,获取菜单的导航数据或来自 CMS 的站点设置),则这些数据在预渲染第一个使用它的页面时只会获取一次,然后缓存起来以供预渲染其他页面时使用。

迁移步骤

确保数据的任何唯一键始终可解析为相同的数据。例如,如果您使用 useAsyncData 获取与特定页面相关的数据,则应提供一个唯一匹配该数据的键。(useFetch 应该会自动为您完成此操作。)

app/pages/test/[slug].vue
// This would be unsafe in a dynamic page (e.g. `[slug].vue`) because the route slug makes a difference
// to the data fetched, but Nuxt can't know that because it's not reflected in the key.
const route = useRoute()
const { data } = await useAsyncData(async () => {
  return await $fetch(`/api/my-page/${route.params.slug}`)
})
// Instead, you should use a key that uniquely identifies the data fetched.
const { data } = await useAsyncData(route.params.slug, async () => {
  return await $fetch(`/api/my-page/${route.params.slug}`)
})

或者,您可以使用以下方法禁用此功能:

nuxt.config.ts
export default 
defineNuxtConfig
({
experimental
: {
sharedPrerenderData
: false
} })

useAsyncDatauseFetch 中的默认 dataerror

🚦 影响级别:最低

更改内容

useAsyncData 返回的 dataerror 对象现在默认为 undefined

更改原因

之前 data 初始化为 null,但在 clearNuxtData 中重置为 undefinederror 初始化为 null。此更改是为了带来更大的一致性。

迁移步骤

如果您正在检查 data.valueerror.value 是否为 null,则可以将这些检查更新为改为检查 undefined

您可以通过运行 npx codemod@latest nuxt/4/default-data-error-value 自动执行此步骤。

如果您遇到任何问题,您可以使用以下方法恢复到之前的行为:

nuxt.config.ts
export default 
defineNuxtConfig
({
experimental
: {
defaults
: {
useAsyncData
: {
value
: 'null',
errorValue
: 'null'
} } } })

如果您这样做,请报告问题,因为我们不打算将其保留为可配置的。

删除调用 useAsyncDatauseFetch 中的 refreshdedupe 选项的已弃用 boolean

🚦 影响级别:最低

更改内容

以前可以将 dedupe: boolean 传递给 refresh。这些是 canceltrue)和 deferfalse)的别名。

app.vue
const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt 3!' }))

async function refreshData () {
  await refresh({ dedupe: true })
}
更改原因

这些别名已删除,以提高清晰度。

在将 dedupe 作为选项添加到 useAsyncData 时出现了这个问题,我们删除了布尔值,因为它们最终变成了 相反的

refresh({ dedupe: false }) 表示“不要取消现有请求以支持此新请求”。但在 useAsyncData 的选项中传递 dedupe: true 表示“如果存在任何挂起的请求,则不要发出任何新请求。”(请参阅 PR。)

迁移步骤

迁移应该很简单

  const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt 3!' }))
  
  async function refreshData () {
-   await refresh({ dedupe: true })
+   await refresh({ dedupe: 'cancel' })

-   await refresh({ dedupe: false })
+   await refresh({ dedupe: 'defer' })
  }
您可以通过运行 npx codemod@latest nuxt/4/deprecated-dedupe-value 自动执行此步骤。

useAsyncDatauseFetch 中清除 data 时尊重默认值

🚦 影响级别:最低

更改内容

如果您为 useAsyncData 提供了一个自定义的 default 值,则现在在调用 clearclearNuxtData 时将使用此值,并且它将重置为其默认值,而不是简单地取消设置。

更改原因

通常,用户会设置一个适当的空值,例如空数组,以避免在迭代它时需要检查 null/undefined。在重置/清除数据时应尊重这一点。

迁移步骤

如果您遇到任何问题,您可以暂时使用以下方法恢复到之前的行为:

nuxt.config.ts
export default 
defineNuxtConfig
({
experimental
: {
resetAsyncDataToUndefined
: true,
} })

如果您这样做,请报告问题,因为我们不打算将其保留为可配置的。

useAsyncDatauseFetch 中进行浅数据响应

🚦 影响级别:最低

useAsyncDatauseFetchuseLazyAsyncDatauseLazyFetch 返回的 data 对象现在是 shallowRef 而不是 ref

更改内容

当获取新数据时,任何依赖于 data 的内容仍然是响应式的,因为整个对象都被替换了。但是,如果您的代码更改了该数据结构 内部 的属性,这将不会触发应用程序中的任何响应性。

更改原因

这为深度嵌套的对象和数组带来了显著的性能提升,因为 Vue 不需要监视每个属性/数组的修改。在大多数情况下,data 也应该是不可变的。

迁移步骤

在大多数情况下,不需要迁移步骤,但是如果您依赖于数据对象的响应性,则有两个选项

  1. 您可以根据每个可组合的基础逐个选择深度响应性
    - const { data } = useFetch('/api/test')
    + const { data } = useFetch('/api/test', { deep: true })
    
  2. 您可以更改项目范围内的默认行为(不建议)
    nuxt.config.ts
    export default 
    defineNuxtConfig
    ({
    experimental
    : {
    defaults
    : {
    useAsyncData
    : {
    deep
    : true
    } } } })
如果需要,您可以通过运行 npx codemod@latest nuxt/4/shallow-function-reactivity 自动执行此步骤。

builder:watch 中使用绝对路径

🚦 影响级别:最低

更改内容

Nuxt builder:watch 钩子现在发出一个路径,该路径是绝对路径,而不是相对于您的项目 srcDir 的相对路径。

更改原因

这使我们能够支持监视位于 srcDir 之外的路径,并为层和其他更复杂的模式提供更好的支持。

迁移步骤

我们已经主动迁移了我们已知使用此钩子的公共 Nuxt 模块。请参阅 问题 #25339

但是,如果您是使用 builder:watch 钩子的模块作者,并且希望保持向后/向前兼容,则可以使用以下代码来确保您的代码在 Nuxt v3 和 Nuxt v4 中都以相同的方式工作

+ import { relative, resolve } from 'node:fs'
  // ...
  nuxt.hook('builder:watch', async (event, path) => {
+   path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path))
    // ...
  })
您可以通过运行 npx codemod@latest nuxt/4/absolute-watch-path 自动执行此步骤。

删除 window.__NUXT__ 对象

更改内容

应用程序完成 hydration 后,我们将删除全局 window.__NUXT__ 对象。

更改原因

这为多应用程序模式打开了大门(#21635)并使我们能够专注于访问 Nuxt 应用程序数据的单一方式 - useNuxtApp()

迁移步骤

数据仍然可用,但可以通过 useNuxtApp().payload 访问。

- console.log(window.__NUXT__)
+ console.log(useNuxtApp().payload)

目录索引扫描

🚦 影响级别:中等

更改内容

您的 middleware/ 文件夹中的子文件夹也会扫描 index 文件,这些文件现在也会在您的项目中注册为中间件。

更改原因

Nuxt 会自动扫描许多文件夹,包括 middleware/plugins/

您的 plugins/ 文件夹中的子文件夹会扫描 index 文件,我们希望使扫描目录之间的行为保持一致。

迁移步骤

可能不需要迁移,但如果您希望恢复到之前的行为,则可以添加一个钩子来过滤掉这些中间件

export default defineNuxtConfig({
  hooks: {
    'app:resolve'(app) {
      app.middleware = app.middleware.filter(mw => !/\/index\.[^/]+$/.test(mw.path))
    }
  }
})

模板编译更改

🚦 影响级别:最低

更改内容

以前,Nuxt 使用 lodash/template 使用 .ejs 文件格式/语法编译位于文件系统上的模板。

此外,我们提供了一些模板实用程序(serializeimportNameimportSources),这些实用程序可用于在这些模板中进行代码生成,这些实用程序现已删除。

更改原因

在 Nuxt v3 中,我们转向了具有 getContents() 函数的“虚拟”语法,该语法更加灵活且性能更高。

此外,lodash/template 出现了一系列安全问题。这些问题实际上并不适用于 Nuxt 项目,因为它是在构建时使用,而不是在运行时使用,并且由受信任的代码使用。但是,它们仍然出现在安全审计中。此外,lodash 是一个庞大的依赖项,大多数项目都不会使用它。

最后,直接在 Nuxt 中提供代码序列化函数并不是理想的。相反,我们维护了像 unjs/knitwork 这样的项目,这些项目可以是您项目的依赖项,并且可以在其中直接报告/解决安全问题,而无需升级 Nuxt 本身。

迁移步骤

我们已提出 PR 以更新使用 EJS 语法的模块,但如果您需要自己执行此操作,则有三种向后/向前兼容的替代方案

+ import { readFileSync } from 'node:fs'
+ import { template } from 'lodash-es'
  // ...
  addTemplate({
    fileName: 'appinsights-vue.js'
    options: { /* some options */ },
-   src: resolver.resolve('./runtime/plugin.ejs'),
+   getContents({ options }) {
+     const contents = readFileSync(resolver.resolve('./runtime/plugin.ejs'), 'utf-8')
+     return template(contents)({ options })
+   },
  })

最后,如果您正在使用模板实用程序(serializeimportNameimportSources),则可以使用以下方法将其替换为 knitwork 中的实用程序

import { genDynamicImport, genImport, genSafeVariableName } from 'knitwork'

const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"{(.+)}"(?=,?$)/gm, r => JSON.parse(r).replace(/^{(.*)}$/, '$1'))

const importSources = (sources: string | string[], { lazy = false } = {}) => {
  return toArray(sources).map((src) => {
    if (lazy) {
      return `const ${genSafeVariableName(src)} = ${genDynamicImport(src, { comment: `webpackChunkName: ${JSON.stringify(src)}` })}`
    }
    return genImport(src, genSafeVariableName(src))
  }).join('\n')
}

const importName = genSafeVariableName
您可以通过运行 npx codemod@latest nuxt/4/template-compilation-changes 自动执行此步骤。

删除实验性功能

🚦 影响级别:最低

更改内容

Nuxt 4 中不再可配置四个实验性功能

  • experimental.treeshakeClientOnly 将为 true(从 v3.0 开始默认为此值)
  • experimental.configSchema 将为 true(从 v3.3 开始默认为此值)
  • experimental.polyfillVueUseHead 将为 false(从 v3.4 开始默认为此值)
  • experimental.respectNoSSRHeader 将为 false(从 v3.4 开始默认为此值)
  • vite.devBundler 不再可配置 - 它将默认使用 vite-node
更改原因

这些选项已设置为其当前值一段时间,我们没有理由相信它们需要保持可配置。

迁移步骤

Nuxt 2 与 Nuxt 3+

在下表中,对 Nuxt 的 3 个版本进行了快速比较

功能 / 版本Nuxt 2Nuxt BridgeNuxt 3+
Vue223
稳定性😊 稳定😊 稳定😊 稳定
性能🏎 快速✈️ 更快🚀 最快
Nitro 引擎
ESM 支持🌙 部分👍 更好
TypeScript☑️ 可选🚧 部分
组合 API🚧 部分
选项 API
组件自动导入
<script setup> 语法🚧 部分
自动导入
webpack445
Vite⚠️ 部分🚧 部分
Nuxi CLI❌ 旧版✅ nuxi✅ nuxi
静态站点

从 Nuxt 2 迁移到 Nuxt 3+

迁移指南提供了 Nuxt 2 功能与 Nuxt 3+ 功能的逐步比较,以及调整您当前应用程序的指导。

查看从 Nuxt 2 迁移到 Nuxt 3 的指南

从 Nuxt 2 迁移到 Nuxt Bridge

如果您希望逐步将您的 Nuxt 2 应用程序迁移到 Nuxt 3,则可以使用 Nuxt Bridge。Nuxt Bridge 是一个兼容性层,允许您在 Nuxt 2 中使用 Nuxt 3+ 功能,并使用选择加入机制。

从 Nuxt 2 迁移到 Nuxt Bridge