升级指南
升级Nuxt
最新版本
要将Nuxt升级到最新版本,使用nuxt upgrade
命令。
npx nuxt upgrade
yarn nuxt upgrade
pnpm nuxt upgrade
bun x nuxt upgrade
夜间发布通道
要使用最新的Nuxt构建版本并在正式发布前测试功能,请阅读夜间发布通道指南。
迁移到Nuxt 4
Nuxt 4包含重大改进和变更。本指南将帮助你将现有的Nuxt 3应用迁移到Nuxt 4。
首先,升级到Nuxt 4:
npm install nuxt@^4.0.0
yarn add nuxt@^4.0.0
pnpm add nuxt@^4.0.0
bun add nuxt@^4.0.0
升级后,大多数Nuxt 4的行为已成为默认值。不过,如果你需要在迁移过程中保持向后兼容性,部分功能仍可配置。
以下部分详细介绍了升级到Nuxt 4所需的关键变更和迁移步骤。
下方记录了突破性或重大变更,以及迁移步骤和可用的配置选项。
使用代码转换工具(Codemods)迁移
为简化升级过程,我们与Codemod团队合作,通过一些开源代码转换工具自动化了许多迁移步骤。
npx codemod feedback
向Codemod团队报告 🙏有关Nuxt 4代码转换工具的完整列表、每个工具的详细信息、来源以及各种运行方式,请访问代码转换工具注册表。
你可以使用以下codemod
命令运行本指南中提到的所有代码转换工具:
npx codemod@latest nuxt/4/migration-recipe
yarn dlx codemod@latest nuxt/4/migration-recipe
pnpm dlx codemod@latest nuxt/4/migration-recipe
bun x codemod@latest nuxt/4/migration-recipe
此命令将按顺序执行所有代码转换工具,并允许你取消选择不希望运行的工具。每个代码转换工具也会在相应的变更下方列出,可单独执行。
新目录结构
🚦 影响级别:重大
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.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/
shared/
server/
api/
middleware/
plugins/
routes/
utils/
nuxt.config.ts
~
别名默认指向app/
目录(即你的srcDir
)。这意味着~/components
解析为app/components/
,~/pages
解析为app/pages/
等。👉 更多细节,请查看实现此变更的PR。
变更原因
- 性能 - 将所有代码放在仓库根目录会导致
.git/
和node_modules/
文件夹被文件监视器扫描/包含,这在非Mac系统上可能显著延迟启动。 - 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({
// 将新的srcDir默认值从`app`恢复为根目录
srcDir: '.',
// 指定`router.options.ts`和`spa-loading-template.html`的目录前缀
dir: {
app: 'app'
}
})
单例数据获取层
🚦 影响级别:中等
变更内容
Nuxt的数据获取系统(useAsyncData
和useFetch
)经过重大重组,以提高性能和一致性:
- 相同键的共享引用:所有使用相同键的
useAsyncData
或useFetch
调用现在共享相同的data
、error
和status
引用。这意味着使用显式键的所有调用不得有冲突的deep
、transform
、pick
、getCachedData
或default
选项。 - 对
getCachedData
的更多控制:getCachedData
函数现在在每次获取数据时都会被调用,即使是由监听器或调用refreshNuxtData
引起的(以前,总是会获取新数据,这种情况下不会调用此函数)。为了更好地控制何时使用缓存数据、何时重新获取,该函数现在接收一个包含请求原因的上下文对象。 - 响应式键支持:现在可以使用计算属性引用、普通引用或 getter函数作为键,这支持自动数据重新获取(并单独存储数据)。
- 数据清理:当最后一个使用
useAsyncData
获取数据的组件卸载时,Nuxt将删除该数据,以避免内存使用不断增长。
变更原因
这些变更旨在改善内存使用,并提高useAsyncData
调用之间加载状态的一致性。
迁移步骤
- 检查不一致的选项:检查任何使用相同键但不同选项或获取函数的组件。
// 现在会触发警告 const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false }) const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true })
将所有使用相同显式键(且有自定义选项)的useAsyncData
调用提取到独立的组合式函数中可能更有利:composables/useUserData.tsexport function useUserData(userId: string) { return useAsyncData( `user-${userId}`, () => fetchUser(userId), { deep: true, transform: (user) => ({ ...user, lastAccessed: new Date() }) } ) }
- 更新
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] + } })
或者,目前你可以通过以下方式禁用此行为:
export default defineNuxtConfig({
experimental: {
granularCachedData: false,
purgeCachedData: false
}
})
图层中模块加载顺序的修正
🚦 影响级别:最小
变更内容
使用Nuxt图层时,模块的加载顺序已修正。以前,项目根目录的模块在扩展图层的模块之前加载,这与预期相反。
现在模块按正确顺序加载:
- 首先是图层模块(按扩展顺序 - 更深层的图层优先)
- 最后是项目模块(最高优先级)
这影响:
- 在
nuxt.config.ts
的modules
数组中定义的模块 - 从
modules/
目录自动发现的模块
变更原因
此变更确保:
- 扩展图层的优先级低于消费项目
- 模块执行顺序与直观的图层继承模式匹配
- 在多层设置中,模块配置和钩子按预期工作
迁移步骤
大多数项目不需要变更,因为这修正了加载顺序以匹配预期行为。
不过,如果你的项目依赖于以前的错误顺序,可能需要:
- 检查模块依赖:检查是否有模块依赖特定的加载顺序
- 调整模块配置:如果模块是为解决错误顺序而配置的
- 彻底测试:确保所有功能在修正后的顺序下正常工作
新正确顺序的示例:
// 图层:my-layer/nuxt.config.ts
export default defineNuxtConfig({
modules: ['layer-module-1', 'layer-module-2']
})
// 项目: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(可覆盖图层模块)
// 4. project-module-2(可覆盖图层模块)
如果遇到因需要注册钩子而导致的模块顺序依赖问题,考虑对需要调用钩子的模块使用modules:done
钩子。它在所有其他模块加载后运行,因此使用是安全的。
👉 更多细节见PR #31507和issue #25719。
路由元数据的去重
🚦 影响级别:最小
变更内容
可以使用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
}
})
Unhead v2
🚦 影响级别:最小
变更内容
用于生成<head>
标签的Unhead已更新到版本2。虽然大部分兼容,但低级别API有一些突破性变更。
- 移除的属性:
vmid
、hid
、children
、body
。 - 不再支持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
的导入更新为#imports
或nuxt/app
。
-import { useHead } from '@unhead/vue'
+import { useHead } from '#imports'
如果仍有问题,可以通过启用head.legacy
配置恢复到v1行为。
export default defineNuxtConfig({
unhead: {
legacy: true,
}
})
SPA加载屏幕的新DOM位置
🚦 影响级别:最小
变更内容
渲染仅客户端页面(ssr: false
)时,我们可以选择渲染加载屏幕(来自~/app/spa-loading-template.html
- 注意在Nuxt 4中已改为~/spa-loading-template.html
),位于Nuxt应用根目录内:
<div id="__nuxt">
<!-- spa加载模板 -->
</div>
现在,我们默认将模板渲染在Nuxt应用根目录旁边:
<div id="__nuxt"></div>
<!-- spa加载模板 -->
变更原因
这允许spa加载模板保留在DOM中,直到Vue应用的suspense解析,防止白屏闪烁。
迁移步骤
如果你的CSS或document.queryElement
定位了spa加载模板,需要更新选择器。为此,你可以使用新的app.spaLoaderTag
和app.spaLoaderAttrs
配置选项。
或者,你可以通过以下方式恢复到以前的行为:
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现在仅内联Vue组件的样式,不内联全局CSS。
变更内容
以前,Nuxt会内联所有CSS,包括全局样式,并移除指向单独CSS文件的<link>
元素。现在,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
会自动为你处理)。
// 在动态页面(如`[slug].vue`)中这样做是不安全的,因为路由slug会影响获取的数据,
// 但Nuxt无法知道这一点,因为键中没有反映出来。
const route = useRoute()
const { data } = await useAsyncData(async () => {
return await $fetch(`/api/my-page/${route.params.slug}`)
})
// 相反,你应该使用唯一标识所获取数据的键。
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'
}
}
}
})
如果需要这样做,请提交issue,因为我们不打算保留此可配置项。
useAsyncData
和useFetch
中refresh
调用时dedupe
选项的已弃用boolean
值 移除
🚦 影响级别:最小
变更内容
以前可以向refresh
传递dedupe: boolean
。这些是cancel
(true
)和defer
(false
)的别名。
const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt!' }))
async function refreshData () {
await refresh({ dedupe: true })
}
变更原因
为了更清晰,这些别名已移除。
在向useAsyncData
添加dedupe
作为选项时出现了问题,我们移除了布尔值,因为它们最终是相反的。
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
。重置/清除数据时应尊重这一点。
useAsyncData
和useFetch
中pending
值的对齐
🚦 影响级别:中等
从useAsyncData
、useFetch
、useLazyAsyncData
和useLazyFetch
返回的pending
对象现在是一个计算属性,仅当status
也为pending时才为true
。
变更内容
现在,当传递immediate: false
时,pending
在第一次请求发出前为false
。这与以前的行为不同,以前pending
在第一次请求发出前始终为true
。
变更原因
这使pending
的含义与status
属性对齐,status
在请求进行中时也为pending
。
迁移步骤
如果你依赖pending
属性,请确保你的逻辑考虑到新行为:pending
仅在状态为pending时为true
。
<template>
- <div v-if="!pending">
+ <div v-if="status === 'success'">
<p>数据:{{ data }}</p>
</div>
<div v-else>
<p>加载中...</p>
</div>
</template>
<script setup lang="ts">
const { data, pending, execute, status } = await useAsyncData(() => fetch('/api/data'), {
immediate: false
})
onMounted(() => execute())
</script>
或者,你可以暂时通过以下方式恢复到以前的行为:
export default defineNuxtConfig({
experimental: {
pendingWhenIdle: true
}
})
useAsyncData
和useFetch
中的键变更行为
🚦 影响级别:中等
变更内容
在useAsyncData
或useFetch
中使用响应式键时,Nuxt会在键变更时自动重新获取数据。当设置immediate: false
时,useAsyncData
仅在数据已获取过一次的情况下,才会在键变更时获取数据。
以前,useFetch
的行为略有不同。它总是会在键变更时获取数据。
现在,useFetch
和useAsyncData
的行为一致 - 仅在数据已获取过一次的情况下,才会在键变更时获取数据。
变更原因
这确保了useAsyncData
和useFetch
之间的行为一致,并防止意外的获取。如果你设置了immediate: false
,则必须调用refresh
或execute
,否则useFetch
或useAsyncData
永远不会获取数据。
迁移步骤
此变更通常会改善预期行为,但如果你期望非即时的useFetch
的键或选项变更时自动触发,现在需要手动触发第一次。
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
}
})
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和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
文件格式/语法的模板。
此外,我们提供了一些模板工具(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中所示。
- 使用
es-toolkit/compat
(lodash template的替代品)作为你的项目的依赖项,而非Nuxt:
+ import { readFileSync } from 'node:fs'
+ import { template } from 'es-toolkit/compat'
// ...
addTemplate({
fileName: 'appinsights-vue.js'
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
自动化此步骤TypeScript配置变更
🚦 影响级别:最小
变更内容
Nuxt现在为不同上下文生成单独的TypeScript配置,以提供更好的类型检查体验:
- 新的TypeScript配置文件:Nuxt现在生成额外的TypeScript配置:
.nuxt/tsconfig.app.json
- 用于应用代码(Vue组件、组合式函数等).nuxt/tsconfig.server.json
- 用于服务器端代码(Nitro/server目录).nuxt/tsconfig.node.json
- 用于构建时代码(模块、nuxt.config.ts
等).nuxt/tsconfig.shared.json
- 用于应用和服务器上下文之间共享的代码(如类型和非环境特定工具).nuxt/tsconfig.json
- 用于向后兼容的遗留配置
- 向后兼容性:扩展
.nuxt/tsconfig.json
的现有项目将继续正常工作。 - 可选的项目引用:新项目或希望获得更好类型检查的项目可以采用TypeScript的项目引用功能。
- 特定上下文的类型检查:每个上下文现在有适当的编译器选项以及针对其特定环境的包含/排除项。
- 新的
typescript.nodeTsConfig
选项:现在可以自定义Node.js构建时代码的TypeScript配置。
变更原因
此变更提供以下好处:
- 更好的类型安全性:每个上下文(应用、服务器、构建时)获得适当的类型检查,带有特定于上下文的全局变量和API。
- 改进的IDE体验:对代码库的不同部分提供更好的智能提示和错误报告。
- 更清晰的分离:服务器代码不会错误地提示客户端API,反之亦然。
- 性能:TypeScript可以更高效地检查具有适当范围配置的代码。
例如,自动导入在nuxt.config.ts
中不可用(但以前TypeScript不会标记这一点)。虽然IDE通过server/
目录中的tsconfig.json
识别了单独的上下文,但这并未反映在类型检查中(需要单独的步骤)。
迁移步骤
无需迁移 - 现有项目将继续正常工作。
不过,要利用改进的类型检查,可以选择采用新的项目引用方法:
- **更新根目录
tsconfig.json
**以使用项目引用:{ "files": [], "references": [ { "path": "./.nuxt/tsconfig.app.json" }, { "path": "./.nuxt/tsconfig.server.json" }, { "path": "./.nuxt/tsconfig.shared.json" }, { "path": "./.nuxt/tsconfig.node.json" } ] }
- **移除任何手动的服务器
tsconfig.json
**文件(如server/tsconfig.json
),这些文件扩展了.nuxt/tsconfig.server.json
。 - 更新类型检查脚本以使用项目引用的构建标志:
- "typecheck": "nuxt prepare && vue-tsc --noEmit" + "typecheck": "nuxt prepare && vue-tsc -b --noEmit"
- 根据需要配置Node.js TypeScript选项:
export default defineNuxtConfig({ typescript: { // 自定义应用/服务器TypeScript配置 tsConfig: { compilerOptions: { strict: true } }, // 自定义构建时TypeScript配置 nodeTsConfig: { compilerOptions: { strict: true } } } })
- 更新任何运行TypeScript检查的CI/构建脚本,确保它们使用新的项目引用方法。
新配置为选择加入的项目提供更好的类型安全性和智能提示,同时为现有设置保持完全的向后兼容性。
移除实验性功能
🚦 影响级别:最小
变更内容
四个实验性功能在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
变更原因
这些选项已设置为当前值一段时间,我们认为没有必要保持它们的可配置性。
迁移步骤
generate
配置 移除顶级
🚦 影响级别:最小
变更内容
Nuxt 4中不再提供顶级generate
配置选项。这包括其所有属性:
generate.exclude
- 用于从预渲染中排除路由generate.routes
- 用于指定要预渲染的路由
变更原因
顶级generate
配置是Nuxt 2的遗留物。我们已经支持nitro.prerender
一段时间了,这是配置预渲染的首选方式。
迁移步骤
用相应的nitro.prerender
选项替换generate
配置:
export default defineNuxtConfig({
- generate: {
- exclude: ['/admin', '/private'],
- routes: ['/sitemap.xml', '/robots.txt']
- }
+ nitro: {
+ prerender: {
+ ignore: ['/admin', '/private'],
+ routes: ['/sitemap.xml', '/robots.txt']
+ }
+ }
})
Nuxt 2 vs. Nuxt 3+
下表快速比较了三个Nuxt版本:
功能 / 版本 | Nuxt 2 | Nuxt Bridge | Nuxt 3+ |
---|---|---|---|
Vue | 2 | 2 | 3 |
稳定性 | 😊 稳定 | 😊 稳定 | 😊 稳定 |
性能 | 🏎 快 | ✈️ 更快 | 🚀 最快 |
Nitro引擎 | ❌ | ✅ | ✅ |
ESM支持 | 🌙 部分 | 👍 更好 | ✅ |
TypeScript | ☑️ 可选 | 🚧 部分 | ✅ |
组合式API | ❌ | 🚧 部分 | ✅ |
选项式API | ✅ | ✅ | ✅ |
组件自动导入 | ✅ | ✅ | ✅ |
<script setup> 语法 | ❌ | 🚧 部分 | ✅ |
自动导入 | ❌ | ✅ | ✅ |
webpack | 4 | 4 | 5 |
Vite | ⚠️ 部分 | 🚧 部分 | ✅ |
Nuxt CLI | ❌ 旧版 | ✅ nuxt | ✅ nuxt |
静态站点 | ✅ | ✅ | ✅ |
Nuxt 2 到 Nuxt 3+
迁移指南提供了Nuxt 2功能与Nuxt 3+功能的逐步比较,以及调整当前应用的指南。
Nuxt 2 到 Nuxt Bridge
如果你希望逐步将Nuxt 2应用迁移到Nuxt 3,可以使用Nuxt Bridge。Nuxt Bridge是一个兼容层,允许你在Nuxt 2中使用Nuxt 3+功能,采用可选机制。