升级指南
升级 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团队合作,通过一些开源 codemods 自动化了许多迁移步骤。
npx codemod feedback
向 Codemod 团队报告 🙏有关 Nuxt 4 codemods 的完整列表、详细信息、其来源以及运行它们的各种方式,请访问Codemod 注册表.
您可以使用以下 codemod
配方运行本指南中提到的所有 codemods
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
此命令将按顺序执行所有 codemods,您可以选择取消选择任何不想运行的 codemod。每个 codemod 及其各自的变更也列在下面,并且可以独立执行。
新目录结构
🚦 影响级别:重大
Nuxt 现在默认使用新的目录结构,并向后兼容(因此如果 Nuxt 检测到您正在使用旧结构,例如顶层 app/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/
shared/
server/
api/
middleware/
plugins/
routes/
utils/
nuxt.config.ts
~
别名现在默认指向 app/
目录(您的 srcDir
)。这意味着 ~/components
解析为 app/components/
,~/pages
解析为 app/pages/
等。👉 欲了解更多详情,请参见实现此更改的 PR.
变更原因
- 性能 - 将所有代码放在仓库根目录会导致
.git/
和node_modules/
文件夹被 FS 观察器扫描/包含,这会显著延迟非 Mac OSes 上的启动。 - IDE 类型安全 -
server/
和您应用程序的其余部分在两个完全不同的上下文中运行,具有不同的全局导入可用,确保server/
不在您应用程序的同一文件夹中是确保您在 IDE 中获得良好自动补全的重要第一步。
迁移步骤
- 创建一个名为
app/
的新目录。 - 将您的
assets/
、components/
、composables/
、app/layouts/
、app/middleware/
、app/pages/
、app/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({
// This reverts the new srcDir default from `app` back to your root directory
srcDir: '.',
// This specifies the directory prefix for `router.options.ts` and `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
调用之间的加载状态一致性。
迁移步骤
- 检查不一致的选项:检查所有使用相同键但选项或获取函数不同的组件。
// This will now trigger a warning const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false }) const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true })
将任何共享显式键(并具有自定义选项)的useAsyncData
调用提取到它们自己的组合函数中可能是有益的app/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 - can be 'initial' | 'refresh:hook' | 'refresh:manual' | 'watch' + + // Example: Don't use cache on manual refresh + if (ctx.cause === 'refresh:manual') return undefined + + return cachedData[key] + } })
或者,现在您可以禁用此行为
export default defineNuxtConfig({
experimental: {
granularCachedData: false,
purgeCachedData: false,
},
})
层中模块加载顺序已更正
🚦 影响级别:最小
变更内容
使用Nuxt 层时,模块的加载顺序已更正。以前,项目根目录中的模块在扩展层中的模块之前加载,这与预期行为相反。
现在模块按正确顺序加载
- 层模块优先(按扩展顺序 - 较深层优先)
- 项目模块最后(最高优先级)
这会影响
- 在
nuxt.config.ts
中的modules
数组中定义的模块 - 从
modules/
目录自动发现的模块
变更原因
此更改确保
- 扩展层的优先级低于消费项目
- 模块执行顺序与直观的层继承模式匹配
- 模块配置和钩子在多层设置中按预期工作
迁移步骤
大多数项目不需要更改,因为这会更正加载顺序以匹配预期行为。
但是,如果您的项目依赖于以前不正确的顺序,您可能需要
- 审查模块依赖关系:检查是否有任何模块依赖于特定的加载顺序
- 调整模块配置:如果模块是为解决不正确的顺序而配置的
- 彻底测试:确保所有功能在更正的顺序下按预期工作
新正确顺序的示例
// 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'],
})
// Loading order (corrected):
// 1. layer-module-1
// 2. layer-module-2
// 3. project-module-1 (can override layer modules)
// 4. project-module-2 (can override layer modules)
如果您因需要注册钩子而遇到模块顺序依赖问题,请考虑对需要调用钩子的模块使用 modules:done
钩子。此钩子在所有其他模块加载后运行,这意味着它是安全使用的。
👉 参阅PR #31507等等问题 #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
在这种情况下,组件名称将是 MyComponent
,就 Vue 而言。如果您想将其与 <KeepAlive>
一起使用,或者在 Vue DevTools 中识别它,您将需要使用此名称。
但为了自动导入它,您需要使用 SomeFolderMyComponent
。
通过此更改,这两个值将匹配,Vue 将生成一个与 Nuxt 组件命名模式匹配的组件名称。
迁移步骤
确保在任何使用 @vue/test-utils
中的 findComponent
的测试中以及任何依赖于组件名称的 <KeepAlive>
中使用更新后的名称。
或者,现在您可以禁用此行为
export default defineNuxtConfig({
experimental: {
normalizeComponentNames: false,
},
})
Unhead v2
🚦 影响级别:最小
变更内容
Unhead,用于生成 <head>
标签,已更新到版本 2。虽然大部分兼容,但它包含几个对低级 API 的重大更改。
- 已删除的 props:
vmid
、hid
、children
、body
。 - 不再支持 Promise 输入。
- 标签现在默认使用 Capo.js 排序。
迁移步骤
以上更改对您的应用程序影响极小。
如果您遇到问题,您应该验证
- 您没有使用任何已删除的 props。
useHead({
meta: [{
name: 'description',
// meta tags don't need a vmid, or a key
- vmid: 'description'
- hid: 'description'
}]
})
import { AliasSortingPlugin, TemplateParamsPlugin } 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
- 请注意,在 Nuxt 4 中这也已更改为 ~/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',
},
})
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,包括全局样式,并删除 <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.
变更原因
此功能自动在预渲染的页面之间共享 payload 数据。这可以在预渲染使用 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
自动化此步骤useAsyncData
和 useFetch
中调用 refresh
时,移除了 dedupe
选项的已弃用 boolean
值 在
🚦 影响级别:最小
变更内容
以前可以将 dedupe: boolean
传递给 refresh
。这些是 cancel
(true
) 和 defer
(false
) 的别名。
const { refresh } = await useAsyncData(() => Promise.resolve({ 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
属性保持一致,后者在请求进行中时也处于 pending
状态。
迁移步骤
如果您依赖 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 })
要退出此行为
// Or globally in your Nuxt config
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)
目录索引扫描
🚦 影响级别:中等
变更内容
您的 app/middleware/
文件夹中的子文件夹也已扫描 index
文件,这些文件现在也作为中间件注册到您的项目中。
变更原因
Nuxt 会自动扫描多个文件夹,包括 app/middleware/
和 app/plugins/
。
您的 app/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: { /* 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
自动化此步骤默认 TypeScript 配置更改
🚦 影响级别:最小
变更内容
compilerOptions.noUncheckedIndexedAccess
现在是 true
而不是 false
。
变更原因
此更改是先前3.12 配置更新的后续,其中我们改进了默认值,主要遵循TotalTypeScript 的建议.
迁移步骤
有两种方法
- 对您的应用程序运行类型检查并修复任何新错误(推荐)。
- 在您的
nuxt.config.ts
中覆盖新默认值export default defineNuxtConfig({ typescript: { tsConfig: { compilerOptions: { noUncheckedIndexedAccess: false, }, }, }, })
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"
- 将所有类型增强移动到其适当的上下文:
- 如果您正在增强应用程序上下文的类型,请将文件移动到
app/
目录。 - 如果您正在增强服务器上下文的类型,请将文件移动到
server/
目录。 - 如果您正在增强在应用程序和服务器之间共享的类型,请将文件移动到
shared/
目录。
从app/
、server/
或shared/
目录外部增强类型将不适用于新的项目引用设置。 - 如果您正在增强应用程序上下文的类型,请将文件移动到
- 根据需要配置 Node.js TypeScript 选项
export default defineNuxtConfig({ typescript: { // Customize app/server TypeScript config tsConfig: { compilerOptions: { strict: true, }, }, // Customize build-time TypeScript config 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
一段时间了,它是 Nuxt 3+ 中配置预渲染的首选方式。
迁移步骤
将 generate
配置替换为相应的 nitro.prerender
选项
export default defineNuxtConfig({
- generate: {
- exclude: ['/admin', '/private'],
- routes: ['/sitemap.xml', '/robots.txt']
- }
+ nitro: {
+ prerender: {
+ ignore: ['/admin', '/private'],
+ routes: ['/sitemap.xml', '/robots.txt']
+ }
+ }
})
Nuxt 2 与 Nuxt 3+
下表快速比较了 Nuxt 的 3 个版本
功能/版本 | Nuxt 2 | Nuxt Bridge | Nuxt 3+ |
---|---|---|---|
Vue | 2 | 2 | 3 |
稳定性 | 😊 稳定 | 😊 稳定 | 😊 稳定 |
性能 | 🏎 快速 | ✈️ 更快 | 🚀 最快 |
Nitro 引擎 | ❌ | ✅ | ✅ |
ESM 支持 | 🌙 部分 | 👍 更好 | ✅ |
TypeScript | ☑️ 选择启用 | 🚧 部分 | ✅ |
组合式 API | ❌ | 🚧 部分 | ✅ |
Options 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+ 功能。