升级指南

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

升级 Nuxt

最新版本

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

npx nuxt upgrade

每日构建版本渠道

要在新特性发布前使用最新的 Nuxt 构建并进行测试,请阅读 每日构建版本渠道 指南。

每日构建版本渠道 latest 标签目前跟踪 Nuxt v4 分支,这意味着现在特别可能存在破坏性更改 —— 请务必小心!你可以通过 "nuxt": "npm:nuxt-nightly@3x" 选择使用 3.x 分支的每日构建版本。

测试 Nuxt 4

Nuxt 4 计划于 2025 年第二季度发布。它将包含所有当前通过 compatibilityVersion: 4 可用的功能。

在正式发布之前,可以从 Nuxt 3.12 版本开始测试许多 Nuxt 4 的破坏性更改。

选择使用 Nuxt 4

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

然后,你可以设置 compatibilityVersion 来匹配 Nuxt 4 的行为:

nuxt.config.ts
export default defineNuxtConfig({
  future: {
    compatibilityVersion: 4,
  },
  // 若要重新启用 _全部_ Nuxt v3 的行为,请设置以下选项:
  // srcDir: '.',
  // dir: {
  //   app: 'app'
  // },
  // experimental: {
  //   scanPageMeta: 'after-resolve',
  //   sharedPrerenderData: false,
  //   compileTemplate: true,
  //   resetAsyncDataToUndefined: true,
  //   templateUtils: true,
  //   relativeWatchPaths: true,
  //   normalizeComponentNames: false,
  //   spaLoadingTemplateLocation: 'within',
  //   parseErrorData: false,
  //   pendingWhenIdle: true,
  //   alwaysRunFetchOnKeyChange: true,
  //   defaults: {
  //     useAsyncData: {
  //       deep: true
  //     }
  //   }
  // },
  // features: {
  //   inlineStyles: true
  // },
  // unhead: {
  //   renderSSRHeadOptions: {
  //     omitLineBreaks: false
  //   }
  // }
})
目前,你需要在每个选择 Nuxt 4 行为的层内定义 compatibilityVersion。在 Nuxt 4 发布后,将不再需要这样做。

当你将 compatibilityVersion 设置为 4 时,Nuxt 配置中的默认值将更改以适应 Nuxt v4 行为,但你可以根据上面注释的行内容,在测试中逐步重新启用 Nuxt v3 的行为。如果遇到问题,请提交 issue,方便我们在 Nuxt 或生态系统中修复。

重大或破坏性变更将会在这里进行说明,并提供向前/向后兼容的迁移步骤。

此部分内容直到最终发布前可能会有变化,如果你正在使用 compatibilityVersion: 4 测试 Nuxt 4,请定期回来查看。

使用 Codemods 迁移

为了简化升级流程,我们与 Codemod 团队合作,提供了一些开源的代码转换工具(codemods)来自动执行许多迁移步骤。

如果遇到任何问题,请通过 npx codemod feedback 向 Codemod 团队反馈 🙏

有关完整的 Nuxt 4 codemods 列表、每个工具的详细信息、源码和多种运行方式,请访问 Codemod 注册表

你可以使用以下 codemod 命令执行本指南中提到的所有 codemods:

npx codemod@latest nuxt/4/migration-recipe

该命令将会按顺序运行所有 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/ 文件夹被文件系统监听器扫描/包含,这可能在非 macOS 系统上显著延长启动时间。
  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 会自动检测。如果没有检测到,请提交 issue。唯一的例外是如果你已经自定义了 srcDir。此时,你需要知道 modules/public/server/ 文件夹会从你的 rootDir 解析,而不是从自定义的 srcDir。如有需要,你可通过配置 dir.modulesdir.publicserverDir 来覆盖。

你也可以通过以下配置强制使用 v3 文件夹结构:

nuxt.config.ts
export default defineNuxtConfig({
  // 将新的 srcDir 默认值从 `app` 改回根目录
  srcDir: '.',
  // 指定 `app/router.options.ts` 和 `app/spa-loading-template.html` 的目录前缀
  dir: {
    app: 'app'
  }
})

单例数据获取层

🚦 影响等级:中等

变化内容

Nuxt 的数据获取系统 (useAsyncDatauseFetch) 已经被大幅重构,以提升性能和一致性:

  1. 相同 key 共享 refs:所有调用 useAsyncDatauseFetch 并使用相同 key 的调用都会共享同一份 dataerrorstatus refs。这意味着所有带显式 key 的调用的选项 deeptransformpickgetCachedDatadefault 不能冲突。
  2. getCachedData 更多控制:现在无论数据是否来自 watcher 还是通过调用 refreshNuxtData,每次请求数据时都会调用 getCachedData。之前这些场景下会强制重新获取数据而不调用该函数。为了更灵活地控制何时使用缓存数据,函数接收的上下文参数中包含了请求来源。
  3. 支持响应式 key:现在可使用计算的 refs、普通 refs 或 getter 函数作为 key,从而支持自动数据刷新(并分开存储数据)。
  4. 数据清理:当最后一个使用了某条 useAsyncData 获取数据的组件卸载时,Nuxt 会移除这条数据,避免内存持续增长。

变更原因

这些修改是为了改善内存使用效率和提升多个调用之间加载状态的一致性。

迁移步骤

  1. 检查不一致的选项:审查任何对相同 key 使用不一致选项或获取函数的组件。
    // 这将触发警告
    const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false })
    const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true })
    

    建议将对同一明确 key 的调用(同时含自定义选项)提取到独立的组合函数中:
    composables/useUserData.ts
    export function useUserData(userId: string) {
      return useAsyncData(
        `user-${userId}`,
        () => fetchUser(userId),
        { 
          deep: true,
          transform: (user) => ({ ...user, lastAccessed: new Date() })
        }
      )
    }
    
  2. 更新 getCachedData 的实现
    useAsyncData('key', fetchFunction, {
    -  getCachedData: (key, nuxtApp) => {
    -    return cachedData[key]
    -  }
    +  getCachedData: (key, nuxtApp, ctx) => {
    +    // ctx.cause - 可能是 'initial' | 'refresh:hook' | 'refresh:manual' | 'watch'
    +    
    +    // 例子:手动刷新时不使用缓存
    +    if (ctx.cause === 'refresh:manual') return undefined
    +    
    +    return cachedData[key]
    +  }
    })
    

你也可以通过以下配置关闭该行为:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    granularCachedData: false,
    purgeCachedData: false
  }
})

路由元数据去重

🚦 影响等级:最低

变化内容

可以通过 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
  }
})

Unhead v2

🚦 影响等级:最低

变化内容

用于生成 <head> 标签的 Unhead 更新至版本 2。虽然大多数兼容,但底层 API 有若干破坏性变更:

  • 移除了属性:vmidhidchildrenbody
  • 不再支持 Promise 类型输入。
  • 标签排序默认使用 Capo.js。

迁移步骤

上述变更对你的应用影响较小。

确保你没有使用已移除的属性。

useHead({
  meta: [{ 
    name: 'description', 
    // meta 标签不再需要 vmid 或 key    
-   vmid: 'description' 
-   hid: 'description'
  }]
})

如果使用了 模板参数别名排序,现在需要显式启用:

import { TemplateParamsPlugin, AliasSortingPlugin } from '@unhead/vue/plugins'

export default defineNuxtPlugin({
  setup() {
    const unhead = injectHead()
    unhead.use(TemplateParamsPlugin)
    unhead.use(AliasSortingPlugin)
  }
})

虽非必须,推荐将 @unhead/vue 的引入改为 #importsnuxt/app

-import { useHead } from '@unhead/vue'
+import { useHead } from '#imports'

如果仍有问题,可通过开启旧版模式恢复 v1 行为:

export default defineNuxtConfig({
  unhead: {
    legacy: true,
  }
})

SPA 加载屏幕的新 DOM 位置

🚦 影响等级:最低

变化内容

在渲染仅客户端页面(ssr: false)时,Nuxt 会在 Nuxt 应用根节点内渲染加载屏幕(取自 app/spa-loading-template.html):

<div id="__nuxt">
  <!-- spa loading template -->
</div>

现在,默认将加载模板渲染在 Nuxt 应用根节点旁边:

<div id="__nuxt"></div>
<!-- spa loading template -->

变更原因

这样做可确保加载模板保留在 DOM 中,直到 Vue 应用的 Suspense 解析完成,避免白屏闪烁。

迁移步骤

如果你使用 CSS 或 document.querySelector 选中加载模板,需要更新选择器。你可以使用新的 app.spaLoaderTagapp.spaLoaderAttrs 配置选项来辅助。

此外可用以下配置恢复之前行为:

nuxt.config.ts
export default defineNuxtConfig({
  experimental: {
    spaLoadingTemplateLocation: 'within',
  }
})

解析 error.data

🚦 影响等级:最低

之前抛出的错误对象中的 data 属性未被解析。现在它被解析后可在 error 对象中使用。虽然这是修正,但如果依赖旧行为并自己解析,则属于破坏性变更。

迁移步骤

更新自定义的 error.vue ,移除对 error.data 的额外解析:

  <script setup lang="ts">
  import type { NuxtError } from '#app'

  const props = defineProps({
    error: Object as () => NuxtError
  })

- const data = JSON.parse(error.data)
+ const data = error.data
  </script>

你也可以通过关闭该实验特性:

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

更精细的内联样式

🚦 影响等级:中等

变化内容

Nuxt 现在只对 Vue 组件内联样式,而非所有全局 CSS。

以前,Nuxt 会将全部 CSS(包括全局样式)内联,并移除 <link> 外链;现在仅对 Vue 组件内联 CSS(这些组件之前会拆分独立 CSS chunk)。这在减少网络请求(首屏不请求单独 .css 文件)的同时,支持缓存单一全局 CSS 文件,减小初始文档大小。

迁移步骤

此特性可配置,你可以通过设置 inlineStyles: true 恢复之前的全局样式内联行为:

nuxt.config.ts
export default defineNuxtConfig({
  features: {
    inlineStyles: true
  }
})

延后扫描页面元数据

🚦 影响等级:最低

变化内容

现在扫描页面元数据(由 definePageMeta 定义)是在调用 pages:extend 钩子后进行,而非之前的钩子之前。

变更原因

这样可扫描用户在 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

变更原因

此特性允许预渲染时在页面间共享 payload 数据,显著提升重复请求相同数据的站点性能,比如每个页面都需要调用 useFetch 获取导航栏数据时,首次获取后缓存用于其他页面。

迁移步骤

确保所有数据唯一的 key 均能映射到对应数据。例如,在动态页面中,useAsyncData 的 key 应能唯一标识获取的数据:

app/pages/test/[slug].vue
// 这是不安全的,因为路由参数 slug 影响数据,但 key 没反映这一点。
const route = useRoute()
const { data } = await useAsyncData(async () => {
  return await $fetch(`/api/my-page/${route.params.slug}`)
})
// 应该使用能唯一标识数据的key。
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 选项的布尔值支持

🚦 影响等级:最低

变化内容

之前可以向 refresh 传递 dedupe: boolean,与 cancel: truedefer: false 别名相同。

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

async function refreshData () {
  await refresh({ dedupe: true })
}

变更原因

为了更清晰,移除了布尔值别名。

因为 refresh({ dedupe: false }) 表示 不取消 当前请求;但 dedupe: trueuseAsyncData 选项中表示如果已有未完成请求则不新建请求,两者含义是 相反 的。(详见 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 清理数据时遵守默认值

🚦 影响等级:最低

变化内容

useAsyncData 提供了自定义的 default 值后,调用 clearclearNuxtData 时会用该默认值重置,而非直接清空。

变更原因

很多用户使用合适的空值(例如空数组)避免在访问数据时检查 nullundefined,应该尊重并保持这类默认值。

迁移步骤

遇问题时可临时用此配置恢复旧行为:

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

此配置未来可能移除,使用时请反馈。

useAsyncDatauseFetchpending 值对齐

🚦 影响等级:中等

useAsyncDatauseFetchuseLazyAsyncDatauseLazyFetch 返回的 pending 现在是一个计算属性,仅当 status 处于 pending 状态时为 true。

变化内容

当设置 immediate: false 时,pending 会在首次请求发起前保持 false,之前是始终 true。

变更原因

pending 现在与 status 属性意义对齐,均表示请求正在进行。

迁移步骤

依赖 pending 的逻辑需考虑新行为,pending 只在 status pending 时为 true。

  <template>
-   <div v-if="!pending">
+   <div v-if="status === 'success'">
      <p>Data: {{ data }}</p>
    </div>
    <div v-else>
      <p>Loading...</p>
    </div>
  </template>
  <script setup lang="ts">
  const { data, pending, execute, status } = await useAsyncData(() => fetch('/api/data'), {
    immediate: false
  })
  onMounted(() => execute())
  </script>

你也可临时恢复旧行为:

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

useAsyncDatauseFetch 中 key 变更行为

🚦 影响等级:中等

变化内容

使用响应式 key 时,Nuxt 在 key 变化时自动重新获取数据。immediate: false 时,useAsyncData 仅在已有数据的情况下才会在 key 变更时重新请求。

之前 useFetch 行为略不同,总是在 key 变更时请求数据。

现在,useFetchuseAsyncData 行为统一,仅在已有数据的情况下响应 key 变化。

变更原因

统一两者行为,避免意外请求。若设置 immediate: false,必须调用 refreshexecute 才能拉取数据。

迁移步骤

新行为通常是提升体验,但若之前依赖 useFetch 在 key 变化时自动请求,首次需手动触发:

  const id = ref('123')
  const { data, execute } = await useFetch('/api/test', {
    query: { id },
    immediate: false
  )
+ watch(id, execute, { once: true })

可全局禁用该行为:

// 或在 Nuxt 配置中
export default defineNuxtConfig({
  experimental: {
    alwaysRunFetchOnKeyChange: true
  }
})

useAsyncDatauseFetch 中的数据浅层响应性

🚦 影响等级:最低

useAsyncDatauseFetchuseLazyAsyncDatauseLazyFetch 返回的 data 现在是 shallowRef 而非 ref

变化内容

新数据到达时,替换 data 对象会触发响应。但修改 data 内部属性不再触发响应。

变更原因

极大提升深层嵌套对象和数组的性能,因为 Vue 不再监听每个属性的变化。大多数场景数据应保持不可变。

迁移步骤

多数情况无需变动,若依赖深层响应,可选:

  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 中的绝对路径监听

🚦 影响等级:最低

变化内容

builder:watch 钩子现在发出的路径为绝对路径(而非相对于 srcDir 的相对路径)。

变更原因

支持监听 srcDir 以外的路径,更好支持层(layers)及复杂场景。

迁移步骤

我们已主动迁移知晓的公共 Nuxt 模块,详见 issue #25339

作为模块作者,想保持兼容可按以下方式保证:

+ 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()

迁移步骤

数据依旧可用,只需改为访问:

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

目录索引扫描改进

🚦 影响等级:中等

变化内容

middleware/ 目录下的子文件夹同样扫描 index 文件,并将其注册为中间件。

变更原因

为使 middleware/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 存在多个安全问题,虽然在构建时调用、由可信代码使用,但仍引发安全审计。lodash 是较大依赖,许多项目不使用。

由 Nuxt 内置提供代码序列化也不理想。我们提供了诸如 unjs/knitwork 这样的项目,可作为项目依赖,便于独立修复安全问题。

迁移步骤

我们已提交 PR 更新使用 EJS 语法的模块,你也可选择以下三种兼容方案:

+ import { readFileSync } from 'node:fs'
+ import { template } from 'es-toolkit/compat'
  // ...
  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 三个版本:

功能 / 版本Nuxt 2Nuxt BridgeNuxt 3+
Vue 版本223
稳定性😊 稳定😊 稳定😊 稳定
性能🏎 快✈️ 更快🚀 最快
Nitro 引擎
ESM 支持🌙 部分支持👍 较好
TypeScript 支持☑️ 可选🚧 部分支持
Composition API🚧 部分支持
Options API
组件自动导入
<script setup> 语法🚧 部分支持
自动导入
webpack 版本445
Vite 支持⚠️ 部分支持🚧 部分支持
Nuxt CLI❌ 老版本nuxtnuxt
静态站点支持

从 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