本篇依然来自于我们的 《前端周刊》 项目!
当一个框架发布大版本时,我们总会期待它带来一些"炸裂"的新功能。但 Nuxt 4 这次走的完全不是这个路子。它没有大肆渲染噱头,而是把重心放在了长期稳定性、性能优化,以及更顺滑的开发者体验上。
Nuxt 4.0 来了:新功能与预期
对于还在用 Nuxt 3 的开发者来说,Nuxt 4 意味着:
-
更快的工作流:文件监听和热更新更敏捷
-
更干净的代码库:项目结构更清晰
-
更可预测的应用:类型系统更严格、更可靠
无论你是在考虑迁移到 Nuxt 4,想体验更完善的 TypeScript 集成,还是对新的数据获取逻辑感兴趣,这篇内容都会拆解重点,并告诉你在升级过程中要避开的坑。
新的 app/
目录
在全新的 Nuxt 4 项目里,最明显的变化就是:所有熟悉的目录(components、pages、layouts......)都搬进了 app/
文件夹。 这样一来,应用代码和项目根目录就完全分开,结构更直观。
Nuxt 3 结构:
Plain
my-nuxt-app/
├─ components/
├─ layouts/
├─ pages/
├─ plugins/
├─ public/
├─ server/
├─ app.vue
└─ nuxt.config.ts
Nuxt 4 结构:
Plain
my-nuxt-app/
├─ app/
│ ├─ components/
│ ├─ layouts/
│ ├─ pages/
│ ├─ plugins/
│ └─ app.vue
├─ public/
├─ server/
└─ nuxt.config.ts
别小看这个调整,它带来了两大好处:
-
文件监听更快
app/
内代码和node_modules/
、.git/
、server/
等根目录文件分开,Vite 的监听范围变小,HMR 更快。尤其在 Windows 和 Linux 上,文件 I/O 的瓶颈能被大大缓解。 -
IDE 上下文更清晰 编辑器能更明确地区分客户端和服务端代码,自动补全和类型推断更准确,跨环境报错更少。
👉 需要说明的是,这并不是强制迁移。Nuxt 4 依然兼容 Nuxt 3 的旧结构。
项目迁移
迁移其实非常轻松:
-
在根目录新建一个
app/
-
把 assets、components、composables、layouts、middleware、pages、plugins、utils 等目录全搬进去
-
app.vue
、error.vue
也丢进app/
完成!Nuxt 会自动识别新结构。
不过,别忘了更新脚本、CI/CD 配置和测试工具里的路径,不然可能踩坑。
-
自定义脚本 / CI/CD
package.json
、GitHub Actions 等配置里的硬编码路径需要更新。 例如:Plain// 旧 "scripts": { "lint": "eslint ./pages" } // 新 "scripts": { "lint": "eslint ./app/pages" }
-
配置文件
.nuxtignore
、测试框架(Vitest、Jest)、Storybook 等配置也要检查路径是否过时。
新的 TypeScript 体验
Nuxt 4 另一大核心升级是 TypeScript 配置彻底重做。
在 Nuxt 3 中,单一的 tsconfig.json
容易导致 类型"串味" (比如服务端的 Node 类型渗透到客户端),从而埋下 bug。 Nuxt 4 把类型系统拆分成 app/ 、server/ 和 shared/ 三个虚拟项目,严格隔离。
好处就是:
-
服务端 API 在客户端用不了,编译阶段就会报错
-
tsconfig.json
也更干净,Nuxt 内部帮你处理路径映射
虽然更严格的类型检查会暴露之前潜伏的错误,但这对长期稳定性是个巨大提升。
Nuxt 3 的 tsconfig.json
相对繁琐:
Plain
{
"extends": "./.nuxt/tsconfig.json",
"compilerOptions": {
"strict": true,
"baseUrl": ".",
"paths": {
"~/*": ["./*"],
"@/*": ["./*"]
}
},
"include": [
"./components/**/*",
"./pages/**/*"
]
}
Nuxt 4 中则更简洁:
Plain
{
"compilerOptions": {
"strict": true
}
}
揭示隐藏错误
更严格的类型检查会把以前没注意到的坑揪出来。举个例子,如果你在浏览器端的代码里不小心用到了 Node 模块(比如 fs
这种只能在服务端用的东西),Nuxt 3 可能不会提醒你,直到运行时才直接崩掉。Nuxt 4 就不一样了,它会在你写代码的时候立刻报错,把问题拦在上线之前。
Plain
// server/utils/log-to-file.ts
import fs from 'node:fs'
export function logToFile(msg: string) {
fs.appendFileSync('server.log', msg + '\n')
}
// 客户端组件
import { logToFile } from '~/server/utils/log-to-file'
logToFile('Button clicked') // ❌ Nuxt 3 运行时崩溃
Nuxt 4 会在编译阶段直接报错: Cannot find module 'node:fs' ...
如果你在开发 Nuxt 模块:
-
分离职责:明确区分客户端和服务端入口
-
检查 exports :在
package.json
中用条件导出(browser / node)保证类型解析正确
数据获取改进
Nuxt 4 改变了 useAsyncData
与 useFetch
的默认行为。
-
Nuxt 3:保留旧数据,同时后台加载新数据 → 避免闪烁,但可能展示过期内容
-
Nuxt 4 :立即清空旧数据,
data
置为null
,pending=true 时明确表示数据无效 → 强调显式加载状态
示例:
Plain
<div v-if="!pending && data">
<h1>{{ data.title }}</h1>
</div>
<div v-else>
Loading...
</div>
如果你仍想用"stale-while-revalidate"模式,可手动实现:
Plain
const displayData = ref(data.value)
watch(data, (newData) => {
if (newData) displayData.value = newData
})
避免 UI 闪烁
在 Nuxt 4 里,数据刷新时可能会出现"UI 闪一下"的情况------原本的内容突然消失,变成一个加载圈,看起来挺突兀。解决办法很简单:要么用骨架屏来占位,让页面结构不变;要么在新数据回来之前,先把旧数据顶上去,避免页面空白。
小结
在一个到处追逐"炫酷特性"的生态里,Nuxt 4 反而选择了成熟与内功:更清晰、更快的项目结构、更严谨的 TypeScript 系统、更合理的数据获取逻辑;
Nuxt 4 的价值不在于单点功能,而是这些改进加在一起带来的质变。它更像是一种长期投资:让大规模应用的开发更快、更稳、更顺畅。