升级指南

了解如何升级到最新的 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
  }
})

修正 Layers 中模块加载顺序

🚦 影响等级:最低

变化内容

使用 Nuxt Layers 时,模块加载顺序被修正。之前项目根目录的模块会优先加载,导致顺序与预期相反。

现在模块加载顺序正确:

  1. Layer 模块先加载(按继承顺序,较深层先)
  2. 项目模块后加载(最高优先级)

影响包括:

  • nuxt.config.tsmodules 数组中声明的模块
  • modules/ 目录的自动发现模块

变更原因

此变化确保:

  • 扩展层优先级低于使用它的项目
  • 模块执行顺序符合直观的层级继承模式
  • 多层结构中模块配置和钩子正常工作

迁移步骤

大多数项目无需更改,该修正使加载顺序与预期一致。

但如果你的项目依赖之前的错误顺序,可能需要:

  1. 检查模块依赖:确认模块加载顺序是否有严格依赖
  2. 调整模块配置:如果有为适应旧顺序的配置,进行调整
  3. 全面测试:确保功能正常且顺序正确

正确顺序示例:

// Layer: my-layer/nuxt.config.ts
export default defineNuxtConfig({
  modules: ['layer-module-1', 'layer-module-2']
})

// Project: nuxt.config.ts
export default defineNuxtConfig({
  extends: ['./my-layer'],
  modules: ['project-module-1', 'project-module-2']
})

// 加载顺序(已修正):
// 1. layer-module-1
// 2. layer-module-2  
// 3. project-module-1(可以覆盖 layer 模块)
// 4. project-module-2(可以覆盖 layer 模块)

如果因需注册钩子而遇到模块加载顺序问题,可考虑使用 modules:done 钩子,该钩子在所有模块加载完成后调用,安全使用。

👉 详情见 PR #31507issue #25719

路由元数据去重

🚦 影响等级:最低

变化内容

通过 definePageMeta 设置的某些路由元数据(如 namepath 等),之前既存在于 route 对象上,也存在于路由元数据中(例如 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-utilsfindComponent)和 <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 加载屏幕(取自 app/spa-loading-template.html)默认渲染位置由:

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

变更为:

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

加载模板不再包裹在 #_nuxt 内侧。

变更原因

此举确保加载模板保留在 DOM 中直到 Vue Suspense 解析完成,防止白屏闪烁。

迁移步骤

若有使用 CSS 或 document.querySelector 针对加载模板,需更新选择器。可用新配置 app.spaLoaderTagapp.spaLoaderAttrs 协助。

若需恢复旧行为:

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

解析 error.data

🚦 影响等级:最低

之前抛出的错误对象中的 data 未被解析。现在该字段被解析后可直接使用。虽然是修正,但对依赖旧行为自行解析数据的用户属于破坏性变更。

迁移步骤

更新自定义的 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,减少初始文档大小。

迁移步骤

你可以通过启用旧行为:

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

延后扫描页面元数据

🚦 影响等级:最低

变化内容

扫描页面元数据(definePageMeta)的时机改为在 pages:extend 钩子之后,而非之前。

变更原因

能扫描 pages:extend 中新增的页面元数据。新增钩子 pages:resolved 用于修改或覆盖元数据。

迁移步骤

如需覆盖页面元数据,应在 pages:resolved 钩子中进行:

  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

变更原因

该功能允许预渲染时在页面间共享数据载荷,显著提升多页面请求同一数据(如导航栏数据)的性能。

迁移步骤

确保所有数据 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(此前分别为 nullnull/undefined)。

变更原因

保证初始化一致性。

迁移步骤

如果代码中检查 null,请改为检查 undefined,例如:

if (data.value === 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,现在移除布尔值支持,仅支持字符串。

变更原因

布尔值别名含义易混淆,且与其他选项冲突。

迁移步骤

用字符串替换:

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

  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 清理数据时遵守默认值

🚦 影响等级:最低

变化内容

调用 clearclearNuxtData 时,若设置了 default 值,则用默认值重置数据,而非直接清空。

变更原因

尊重用户自定义默认空值,避免频繁检查 nullundefined

迁移步骤

遇问题可暂用配置关闭该行为:

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

未来该配置可能移除。

useAsyncDatauseFetchpending 值对齐

🚦 影响等级:中等

useAsyncDatauseFetchuseLazyAsyncDatauseLazyFetch 返回的 pending 现在为计算属性,且仅在 statuspending 时返回 true

变化内容

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

变更原因

保证 pendingstatus 一致,均表示请求中状态。

迁移步骤

依赖 pending 的逻辑需遵循新语义,示例:

  <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 变化时,useAsyncData 仅在已有数据时重新请求数据,符合 immediate: false 语义。useFetch 行为现在与之保持一致,均不会无条件请求。

变更原因

统一行为防止意外请求。关闭自动请求时,需手动调用 refreshexecute

迁移步骤

如依赖旧行为自动请求,可手动注册监听:

  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 返回的 dataref 变为 shallowRef

变化内容

新数据到达时替换 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 中的绝对路径监听

🚦 影响等级:最低

变化内容

builder:watch 钩子回调的路径现为绝对路径(非相对于 srcDir 的相对路径)。

变更原因

支持监听 srcDir 以外路径,更好支持多层和复杂场景。

迁移步骤

我们已自动迁移公共 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),并统一通过 useNuxtApp() 访问 Nuxt 数据。

迁移步骤

数据仍可访问,改为:

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

目录索引扫描改进

🚦 影响等级:中等

变化内容

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

变更原因

plugins/ 目录一致,支持子文件夹中的 index 文件。

迁移步骤

通常无须迁移。如想恢复旧行为,可在钩子中过滤掉 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 存在安全问题,且依赖较大。内置使用代码序列化不理想,推荐使用 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