升级指南
升级 Nuxt
最新版本
要将 Nuxt 升级到最新版本,请使用 nuxi upgrade
命令。
npx nuxi upgrade
每日构建发布频道
要使用最新的 Nuxt 构建并测试发布前的功能,请阅读每日构建发布频道 指南。
latest
标签目前正在跟踪 Nuxt v4 分支,这意味着它现在特别容易出现重大更改 - 请小心!您可以使用 "nuxt": "npm:nuxt-nightly@3x"
选择加入 3.x 分支的每日构建版本。测试 Nuxt 4
Nuxt 4 的发布日期待定。这取决于 Nitro 的主要版本发布后是否有足够的时间在社区中进行适当的测试。您可以在 此 PR 中跟踪 Nitro 版本发布的进展情况。
在发布之前,可以从 Nuxt 3.12+ 版本开始测试 Nuxt 4 的许多重大更改。
选择加入 Nuxt 4
首先,将 Nuxt 升级到最新版本。
然后,您可以设置您的 compatibilityVersion
以匹配 Nuxt 4 的行为。
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,
// spaLoadingTemplateLocation: 'within',
// defaults: {
// useAsyncData: {
// deep: true
// }
// }
// },
// features: {
// inlineStyles: true
// },
// unhead: {
// renderSSRHeadOptions: {
// omitLineBreaks: false
// }
// }
})
当您将 compatibilityVersion
设置为 4
时,整个 Nuxt 配置中的默认设置将更改为选择使用 Nuxt v4 的行为,但在测试时,您可以按照上面注释掉的行,细粒度地重新启用 Nuxt v3 的行为。如果遇到问题,请提交 issue,以便我们可以在 Nuxt 或生态系统中解决它们。
迁移到 Nuxt 4
此处将记录重大或突破性更改,以及向后/向前兼容的迁移步骤。
compatibilityVersion: 4
测试 Nuxt 4,请定期返回此处查看。使用 Codemod 进行迁移
为了方便升级过程,我们与 Codemod 团队合作,使用一些开源的 codemod 自动化许多迁移步骤。
npx codemod feedback
向 Codemod 团队报告 🙏有关 Nuxt 4 codemod 的完整列表、每个 codemod 的详细信息、其来源以及各种运行方式,请访问 Codemod Registry。
您可以使用以下 codemod
配方运行本指南中提到的所有 codemod
npx codemod@latest nuxt/4/migration-recipe
此命令将按顺序执行所有 codemod,您可以选择取消选择任何您不想运行的 codemod。每个 codemod 也列在下面,并附有各自的更改,并且可以独立执行。
新的目录结构
🚦 影响级别:重大
Nuxt 现在默认使用新的目录结构,具有向后兼容性(因此,如果 Nuxt 检测到您正在使用旧的结构,例如顶级的 pages/
目录,则此新结构将不适用)。
发生了什么变化
- 新的 Nuxt 默认
srcDir
默认为app/
,并且大多数内容都从那里解析。 serverDir
现在默认为<rootDir>/server
而不是<srcDir>/server
layers/
、modules/
和public/
默认相对于<rootDir>
解析- 如果使用 Nuxt Content v2.13+,则
content/
相对于<rootDir>
解析 - 添加了一个新的
dir.app
,它是我们查找router.options.ts
和spa-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。
更改原因
- 性能 - 将所有代码放置在存储库的根目录会导致
.git/
和node_modules/
文件夹被 FS 监视器扫描/包含,这会显着延迟非 Mac OS 上的启动。 - IDE 类型安全 -
server/
和应用程序的其余部分在两个完全不同的上下文中使用不同的全局导入运行,并且确保server/
不在与应用程序其余部分相同的文件夹内部是确保您在 IDE 中获得良好自动完成功能的第一大步。
迁移步骤
- 创建一个名为
app/
的新目录。 - 将您的
assets/
、components/
、composables/
、layouts/
、middleware/
、pages/
、plugins/
和utils/
文件夹以及app.vue
、error.vue
和app.config.ts
移动到该目录下。如果您有app/router-options.ts
或app/spa-loading-template.html
,则这些路径保持不变。 - 确保您的
nuxt.config.ts
、content/
、layers/
、modules/
、public/
和server/
文件夹保留在app/
文件夹之外,位于项目的根目录中。 - 请记住更新任何第三方配置文件以使用新的目录结构,例如您的
tailwindcss
或eslint
配置(如果需要 -@nuxtjs/tailwindcss
应该会自动正确配置tailwindcss
)。
npx codemod@latest nuxt/4/file-structure
自动完成此迁移但是,迁移是不需要的。如果您希望保留当前的文件夹结构,Nuxt 应该会自动检测到它。(如果未检测到,请提出 issue。)唯一的例外是,如果您已经有一个自定义的 srcDir
。在这种情况下,您应该知道您的 modules/
、public/
和 server/
文件夹将从您的 rootDir
而不是从您的自定义 srcDir
解析。如果您需要,可以通过配置 dir.modules
、dir.public
和 serverDir
来覆盖此设置。
您还可以使用以下配置强制使用 v3 文件夹结构
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
设置一些路由元数据,例如 name
、path
等。以前,这些元数据既可以在路由上,也可以在路由元数据上使用(例如,route.name
和 route.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>
中使用更新后的名称。
或者,目前,您可以使用以下代码禁用此行为
export default defineNuxtConfig({
experimental: {
normalizeComponentNames: false
}
})
SPA 加载屏幕的新 DOM 位置
🚦 影响级别:最小
发生了什么变化
在呈现仅客户端页面时(使用 ssr: false
),我们会在 Nuxt 应用程序根目录中选择性地呈现一个加载屏幕(来自 app/spa-loading-template.html
)。
<div id="__nuxt">
<!-- spa loading template -->
</div>
现在,我们默认在 Nuxt 应用程序根目录旁边呈现模板
<div id="__nuxt"></div>
<!-- spa loading template -->
更改原因
这允许 SPA 加载模板保留在 DOM 中,直到 Vue 应用程序的 suspense 解析完毕,从而防止出现白屏闪烁。
迁移步骤
如果您使用 CSS 或 document.queryElement
定位 SPA 加载模板,则需要更新您的选择器。为此,您可以使用新的 app.spaLoaderTag
和 app.spaLoaderAttrs
配置选项。
或者,您可以使用以下代码恢复到以前的行为
export default defineNuxtConfig({
experimental: {
spaLoadingTemplateLocation: 'within',
}
})
更精细的内联样式
🚦 影响级别:中等
Nuxt 现在只会内联 Vue 组件的样式,而不是全局 CSS。
发生了什么变化
以前,Nuxt 会内联所有 CSS,包括全局样式,并删除 <link>
元素以分隔 CSS 文件。现在,Nuxt 只会对 Vue 组件执行此操作(以前会生成单独的 CSS 块)。我们认为这在减少单独的网络请求方面(就像以前一样,初始加载时不会有针对每个页面或每个组件的单独 .css
文件请求)以及允许缓存单个全局 CSS 文件并减少初始请求的文档下载大小方面,取得了更好的平衡。
迁移步骤
此功能是完全可配置的,您可以通过将 inlineStyles: true
设置为内联全局 CSS 以及每个组件的 CSS 来恢复到以前的行为。
export default defineNuxtConfig({
features: {
inlineStyles: true
}
})
解析后扫描页面元数据
🚦 影响级别:最小
发生了什么变化
我们现在在调用 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'
}
}
})
或者,您可以使用以下代码恢复到以前的行为
export default defineNuxtConfig({
experimental: {
scanPageMeta: true
}
})
共享预渲染数据
🚦 影响级别:中等
发生了什么变化
我们启用了一个以前的实验性功能,以在不同的页面之间共享来自 useAsyncData
和 useFetch
调用的数据。请参阅 原始 PR。
更改原因
此功能会自动在预渲染的页面之间共享有效负载数据。当预渲染使用 useAsyncData
或 useFetch
并在不同页面中提取相同数据的站点时,这可以带来显着的性能改进。
例如,如果您的站点需要每个页面都执行 useFetch
调用(例如,从 CMS 获取菜单的导航数据或站点设置),则该数据仅会在预渲染第一个使用它的页面时获取一次,然后缓存以在预渲染其他页面时使用。
迁移步骤
请确保您的数据的任何唯一键始终可以解析为相同的数据。例如,如果您正在使用 useAsyncData
来获取与特定页面相关的数据,则应提供唯一匹配该数据的键。(useFetch
应该会自动为您执行此操作。)
// 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}`)
})
或者,您可以使用以下配置禁用此功能:
export default defineNuxtConfig({
experimental: {
sharedPrerenderData: false
}
})
useAsyncData
和 useFetch
中的默认 data
和 error
值
🚦 影响级别:最小
发生了什么变化
现在,从 useAsyncData
返回的 data
和 error
对象将默认为 undefined
。
更改原因
之前,data
初始化为 null
,但在 clearNuxtData
中重置为 undefined
。error
初始化为 null
。此更改是为了提高一致性。
迁移步骤
如果您之前检查 data.value
或 error.value
是否为 null
,则现在可以将这些检查更新为检查 undefined
。
npx codemod@latest nuxt/4/default-data-error-value
来自动化此步骤如果遇到任何问题,您可以使用以下配置恢复到之前的行为:
export default defineNuxtConfig({
experimental: {
defaults: {
useAsyncData: {
value: 'null',
errorValue: 'null'
}
}
}
})
如果您正在执行此操作,请报告问题,因为我们不打算将其保留为可配置项。
删除在 useAsyncData
和 useFetch
中调用 refresh
时,dedupe
选项的已弃用 boolean
值
🚦 影响级别:最小
发生了什么变化
以前,可以将 dedupe: boolean
传递给 refresh
。这些是 cancel
(true
) 和 defer
(false
) 的别名。
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
来自动化此步骤在 useAsyncData
和 useFetch
中清除 data
时遵守默认值
🚦 影响级别:最小
发生了什么变化
如果您为 useAsyncData
提供了自定义的 default
值,则在调用 clear
或 clearNuxtData
时将使用此值,并且它将被重置为其默认值,而不是简单地取消设置。
更改原因
用户通常会设置适当的空值(例如空数组),以避免在迭代时需要检查 null
/undefined
。在重置/清除数据时应遵循此规则。
迁移步骤
如果遇到任何问题,您可以使用以下配置暂时恢复到之前的行为:
export default defineNuxtConfig({
experimental: {
resetAsyncDataToUndefined: true,
}
})
如果您正在执行此操作,请报告问题,因为我们不打算将其保留为可配置项。
useAsyncData
和 useFetch
中的浅数据响应式
🚦 影响级别:最小
从 useAsyncData
、useFetch
、useLazyAsyncData
和 useLazyFetch
返回的 data
对象现在是 shallowRef
而不是 ref
。
发生了什么变化
当获取新数据时,任何依赖于 data
的内容仍然是响应式的,因为整个对象都被替换了。但是,如果您的代码更改了该数据结构内部的属性,则不会在您的应用程序中触发任何响应式。
更改原因
这为深度嵌套的对象和数组带来了显著的性能提升,因为 Vue 不需要监视每个属性/数组的修改。在大多数情况下,data
也应该是不可变的。
迁移步骤
在大多数情况下,不需要迁移步骤,但是如果您依赖于数据对象的响应式,则有两种选择
- 您可以基于每个组合式选择性地启用深度响应式
- const { data } = useFetch('/api/test') + const { data } = useFetch('/api/test', { deep: true })
- 您可以在整个项目范围内更改默认行为(不推荐)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 模块。请参阅 issue #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__
对象
发生了什么变化
我们正在删除应用程序完成水合后的全局 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
文件格式/语法。
此外,我们提供了一些模板实用程序(serialize
、importName
、importSources
),这些实用程序可用于这些模板中的代码生成,但现在已被删除。
更改原因
在 Nuxt v3 中,我们转向使用 getContents()
函数的“虚拟”语法,这种语法更加灵活和高性能。
此外,lodash/template
出现了一系列的安全性问题。这些问题实际上并不适用于 Nuxt 项目,因为它是在构建时而不是运行时使用的,并且由受信任的代码使用。但是,它们仍然会出现在安全审核中。此外,lodash
是一个庞大的依赖项,并且大多数项目都不使用它。
最后,直接在 Nuxt 中提供代码序列化函数并不理想。相反,我们维护了诸如 unjs/knitwork 之类的项目,这些项目可以是您的项目的依赖项,并且可以在其中直接报告/解决安全问题,而无需升级 Nuxt 本身。
迁移步骤
我们已经提出了 PR 来更新使用 EJS 语法的模块,但是如果您需要自己执行此操作,则可以使用以下三种向后/向前兼容的替代方法:
- 将字符串插值逻辑直接移动到
getContents()
中。 - 使用自定义函数来处理替换,如 https://github.com/nuxt-modules/color-mode/pull/240 中所示。
- 继续使用
lodash
,作为您项目的依赖项,而不是 Nuxt 的依赖项
+ 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 })
+ },
})
最后,如果您正在使用模板实用程序(serialize
、importName
、importSources
),则可以使用来自 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 2 | Nuxt Bridge | Nuxt 3+ |
---|---|---|---|
Vue | 2 | 2 | 3 |
稳定性 | 😊 稳定 | 😊 稳定 | 😊 稳定 |
性能 | 🏎 快 | ✈️ 更快 | 🚀 最快 |
Nitro 引擎 | ❌ | ✅ | ✅ |
ESM 支持 | 🌙 部分 | 👍 更好 | ✅ |
TypeScript | ☑️ 可选 | 🚧 部分 | ✅ |
Composition API | ❌ | 🚧 部分 | ✅ |
Options API | ✅ | ✅ | ✅ |
组件自动导入 | ✅ | ✅ | ✅ |
<script setup> 语法 | ❌ | 🚧 部分 | ✅ |
自动导入 | ❌ | ✅ | ✅ |
webpack | 4 | 4 | 5 |
Vite | ⚠️ 部分 | 🚧 部分 | ✅ |
Nuxi CLI | ❌ 旧 | ✅ nuxi | ✅ nuxi |
静态站点 | ✅ | ✅ | ✅ |
Nuxt 2 到 Nuxt 3+
迁移指南提供了 Nuxt 2 功能与 Nuxt 3+ 功能的逐步比较,并提供了调整当前应用程序的指导。
Nuxt 2 到 Nuxt Bridge
如果您希望逐步将 Nuxt 2 应用程序迁移到 Nuxt 3,则可以使用 Nuxt Bridge。Nuxt Bridge 是一个兼容性层,允许您在 Nuxt 2 中使用 Nuxt 3+ 功能,并具有可选机制。