升级指南
了解如何升级到最新的 Nuxt 版本。
升级 Nuxt
最新版本
要将 Nuxt 升级到最新版本,请使用 nuxt upgrade
命令。
npx nuxt upgrade
每夜发布频道
要使用最新的 Nuxt 构建并在发布前测试功能,请阅读每夜发布频道指南。
每夜发布频道的 latest
标签目前正在跟踪 Nuxt v4 分支,这意味着现在特别可能有重大更改 — 请小心!您可以选择加入 3.x 分支的每夜发布,使用 "nuxt": "npm:nuxt-nightly@3x"
。
测试 Nuxt 4
Nuxt 4 计划于 2025 年第二季度发布。它将包含目前通过 compatibilityVersion: 4
提供的所有功能。
在发布之前,可以从 Nuxt 版本 3.12+ 测试 Nuxt 4 的许多重大更改。
选择加入 Nuxt 4
首先,将 Nuxt 升级到最新版本。
然后,您可以设置 compatibilityVersion
以匹配 Nuxt 4 的行为:
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 行为的层中定义兼容性版本。在 Nuxt 4 发布后,这将不再需要。
当您将 compatibilityVersion
设置为 4
时,您的 Nuxt 配置中的默认值将更改为选择加入 Nuxt v4 行为,但您可以在测试时逐步重新启用 Nuxt v3 行为,遵循上面的注释行。如果是这样,请提交问题,以便我们可以在 Nuxt 或生态系统中解决它们。
重大或重要的更改将在此处注明,并附有向后/向前兼容的迁移步骤。
在最终发布之前,此部分可能会更改,因此如果您正在使用 compatibilityVersion: 4
测试 Nuxt 4,请定期回来查看。
使用 Codemods 进行迁移
为了简化升级过程,我们与 Codemod 团队合作,通过一些开源的 codemods 自动化许多迁移步骤。
如果您遇到任何问题,请使用 npx codemod feedback
向 Codemod 团队报告 🙏
有关 Nuxt 4 codemods 的完整列表、每个 codemod 的详细信息、其来源以及各种运行方式,请访问 Codemod Registry。
您可以使用以下 codemod
配方运行本指南中提到的所有 codemods:
npx codemod@latest nuxt/4/migration-recipe
此命令将按顺序执行所有 codemods,并提供取消选择您不希望运行的选项。每个 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 操作系统上的启动。 - 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 应该会自动检测到它。(如果没有,请提出问题。)唯一的例外是,如果您 已经 有一个自定义的 srcDir
。在这种情况下,您应该知道您的 modules/
、public/
和 server/
文件夹将从您的 rootDir
而不是自定义的 srcDir
解析。如果需要,您可以通过配置 dir.modules
、dir.public
和 serverDir
来覆盖此设置。
您还可以通过以下配置强制使用 v3 文件夹结构:
export default defineNuxtConfig({
// 这将新的 srcDir 默认值从 `app` 恢复到您的根目录
srcDir: '.',
// 这指定了 `app/router.options.ts` 和 `app/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
}
})
路由元数据的去重
🚦 影响级别:最小
更改内容
可以使用 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',
// 元标签不需要 vmid 或 key
- vmid: 'description'
- hid: 'description'
}]
})
- 如果您使用 Template Params 或 Alias Tag Sorting,您现在需要显式选择加入这些功能。
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
)时,我们可以选择在 Nuxt 应用程序根目录内渲染加载屏幕(来自 app/spa-loading-template.html
):
<div id="__nuxt">
<!-- spa 加载模板 -->
</div>
现在,我们默认将模板渲染在 Nuxt 应用程序根目录旁边:
<div id="__nuxt"></div>
<!-- spa 加载模板 -->
更改原因
这允许 spa 加载模板在 Vue 应用程序 suspense 解析之前保留在 DOM 中,防止出现白屏。
迁移步骤
如果您使用 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>
或者,您可以禁用此更改:
export default defineNuxtConfig({
experimental: {
parseErrorData: false
},
})
更细粒度的内联样式
🚦 影响级别:中等
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'
}
}
}
})
如果您这样做,请报告问题,因为我们不打算保持此配置项。
删除 useAsyncData
和 useFetch
中调用 refresh
时 dedupe
选项的已弃用 boolean
值
🚦 影响级别:最小
更改内容
以前可以将 dedupe: boolean
传递给 refresh
。这些是 cancel
(true
)和 defer
(false
)的别名。
const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt!' }))
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
中 pending
值的对齐
🚦 影响级别:中等
从 useAsyncData
、useFetch
、useLazyAsyncData
和 useLazyFetch
返回的 pending
对象现在是一个计算属性,仅在 status
也处于挂起状态时为 true
。
更改内容
现在,当传递 immediate: false
时,pending
将在第一次请求发出之前为 false
。这与之前的行为不同,之前 pending
总是为 true
,直到第一次请求发出。
更改原因
这使 pending
的含义与 status
属性对齐,后者在请求进行中时也为 pending
。
迁移步骤
如果您依赖于 pending
属性,请确保您的逻辑考虑到新的行为,其中 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>
或者,您可以暂时恢复到以前的行为:
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 模块。请参阅问题 #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 中。
- 使用
es-toolkit/compat
(lodash 模板的替代品),作为 您的 项目的依赖项,而不是 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
自动执行此步骤
移除实验功能
🚦 影响级别:最小
更改内容
四个实验功能在 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 | ☑️ 可选 | 🚧 部分支持 | ✅ |
组合 API | ❌ | 🚧 部分支持 | ✅ |
选项 API | ✅ | ✅ | ✅ |
组件自动导入 | ✅ | ✅ | ✅ |
<script setup> 语法 | ❌ | 🚧 部分支持 | ✅ |
自动导入 | ❌ | ✅ | ✅ |
webpack | 4 | 4 | 5 |
Vite | ⚠️ 部分支持 | 🚧 部分支持 | ✅ |
Nuxt CLI | ❌ 旧版 | ✅ nuxt | ✅ nuxt |
静态网站 | ✅ | ✅ | ✅ |
Nuxt 2 到 Nuxt 3+
迁移指南提供了 Nuxt 2 功能与 Nuxt 3+ 功能的逐步比较,并指导您适应当前的应用程序。
另请参阅 migration > overviewNuxt 2 到 Nuxt Bridge
如果您希望逐步将 Nuxt 2 应用程序迁移到 Nuxt 3,可以使用 Nuxt Bridge。Nuxt Bridge 是一个兼容层,允许您在 Nuxt 2 中使用 Nuxt 3+ 功能,并具有选择加入机制。
另请参阅 bridge > overview