Nuxt4 从入门到精通:终极SSR全栈开发指南
2026年Nuxt4 已经成为 Vue 生态中最成熟、最高效的全栈框架。它彻底解决了传统 SSR 开发的所有痛点将「服务端渲染的 SEO 优势」与「客户端 SPA 的丝滑体验」完美融合同时提供了开箱即用的工程化能力。本文将从零基础开始带你全面掌握 Nuxt4 的核心概念、实战技巧与最佳实践最终构建一个高性能的文章系统完美适配 ThinkPHP 后端。一、初识 Nuxt4为什么它是现代 Web 开发的首选Nuxt4 是基于 Vue 3 和 Nitro 2.0 构建的全栈应用框架它的核心定位是「让开发者专注于业务逻辑无需关心复杂的 SSR 配置与工程化问题」。1.1 Nuxt4 的核心优势零配置 SSR开箱即用的服务端渲染能力无需手动搭建 Node 服务器和 webpack 配置岛屿式水合彻底解决传统全量水合的性能问题和 DOM 不匹配警告全栈开发体验同一项目中同时编写前端页面和后端 API无缝衔接极致性能Nitro 2.0 引擎带来 60% 的渲染速度提升和 40% 的内存占用降低自动导入组件、组合式函数、API 自动导入无需手动 import多环境部署一次编写可部署到 Node.js 服务器、静态托管、Serverless、边缘函数等多种环境1.2 环境搭建与项目初始化Nuxt4 要求 Node.js 版本 ≥ 20.x使用官方脚手架一键创建项目# 创建最新版 Nuxt4 项目npx nuxi initlatest my-nuxt-app# 进入项目目录cdmy-nuxt-app# 安装依赖npminstall# 启动开发服务器npmrun dev启动成功后访问http://localhost:3000即可看到 Nuxt4 的欢迎页面。1.3 Nuxt4 标准项目结构Nuxt4 采用了更清晰的app/目录结构将应用代码集中管理前后端分离更明确my-nuxt-app/ ├── app/ # 应用核心代码 │ ├── components/ # 全局组件自动导入 │ ├── pages/ # 页面组件约定式路由 │ ├── layouts/ # 布局组件 │ ├── composables/ # 组合式函数自动导入 │ └── app.vue # 应用入口组件 ├── public/ # 静态资源直接映射到根路径 ├── server/ # 服务端代码API 路由、中间件 │ ├── api/ # API 接口 │ └── middleware/ # 服务端中间件 ├── shared/ # 前后端共享工具函数 ├── nuxt.config.ts # Nuxt 配置文件 └── tsconfig.json # TypeScript 配置二、核心概念约定大于配置的开发哲学Nuxt4 遵循「约定大于配置」的原则通过目录结构自动生成路由、组件导入等功能极大提升了开发效率。2.1 约定式路由无需手动配置路由表Nuxt4 会根据app/pages/目录下的文件结构自动生成路由无需手动编写路由配置。基础路由app/pages/index.vue→ 对应路由/app/pages/article/list.vue→ 对应路由/article/listapp/pages/about.vue→ 对应路由/about动态路由使用方括号[]定义动态路由参数app/pages/article/[id].vue→ 对应路由/article/1、/article/2在组件中通过useRoute()获取参数script setup langts const route useRoute() const articleId route.params.id // 获取动态参数 id /script嵌套路由创建与文件同名的目录即可实现嵌套路由app/pages/article.vueapp/pages/article/[id].vue→ 嵌套路由结构在父组件中使用NuxtPage /显示子路由内容2.2 布局系统统一页面结构Nuxt4 的布局系统允许你定义多个全局布局不同页面可以使用不同的布局。默认布局创建app/layouts/default.vue所有页面默认使用该布局template div classapp header网站头部/header main NuxtPage / !-- 页面内容将在这里渲染 -- /main footer网站底部/footer /div /template自定义布局创建app/layouts/admin.vue在页面中通过definePageMeta指定使用该布局!-- app/pages/admin/dashboard.vue -- script setup langts definePageMeta({ layout: admin // 使用 admin 布局 }) /script2.3 自动导入告别繁琐的 importNuxt4 会自动导入以下目录中的内容无需手动 importapp/components/所有 Vue 组件app/composables/所有组合式函数server/api/所有 API 接口通过$fetch调用例如创建app/components/ArticleCard.vue后可以直接在任何页面中使用template ArticleCard :articleitem / /template三、SSR 核心原理岛屿式水合的革命性突破Nuxt4 最大的创新是岛屿式水合Island Hydration它彻底解决了传统全量水合的性能问题和 DOM 不匹配警告。3.1 传统全量水合的痛点传统 SSR 框架采用「全量水合」模式服务端渲染完整 HTML 并返回给浏览器浏览器下载客户端 JS 包对整个页面的所有 DOM 节点进行比对和水合绑定事件和响应式这种模式存在三个致命问题性能差水合过程耗时久尤其是复杂页面易出错任何微小的 DOM 不一致都会导致水合失败事件绑定失效资源浪费纯静态内容也需要水合增加了 JS 包体积3.2 Nuxt4 岛屿式水合的核心逻辑Nuxt4 将页面拆分为静态岛屿和交互岛屿静态岛屿纯展示内容文章标题、正文、图片永不水合只在服务端渲染为静态 HTML不发送任何客户端 JS交互岛屿需要用户交互的组件分页按钮、点赞按钮、评论框仅局部水合只对这些组件进行事件绑定和响应式处理这种模式带来了革命性的提升水合耗时减少 90%只水合需要交互的组件彻底消除 DOM 不匹配警告静态内容不参与水合比对JS 包体积大幅减小纯静态内容不需要发送客户端 JS首屏可交互时间TTI显著提升页面加载后立即可以交互3.3 岛屿式水合的使用方式Nuxt4 默认开启岛屿式水合无需额外配置。你只需要遵循一个简单的原则纯展示内容直接写在页面中交互逻辑封装成独立组件例如文章列表页的实现!-- app/pages/article/list.vue -- script setup langts // 服务端获取文章列表数据 const { data: articleList } await useServerFetch(/api/article/list, { query: { page: 1, limit: 10 } }) /script template div classarticle-list !-- 静态岛屿文章列表纯展示永不水合 -- div v-foritem in articleList :keyitem.id classarticle-item h2{{ item.title }}/h2 p{{ item.description }}/p !-- 交互岛屿点赞按钮仅局部水合 -- LikeButton :article-iditem.id :like-countitem.likeCount / /div !-- 交互岛屿分页组件仅局部水合 -- Pagination :totaltotal :pagepage changegetPageData / /div /template四、数据请求SSR 友好的全栈数据获取方案Nuxt4 提供了三个核心数据获取 API完美适配 SSR 场景解决了传统数据请求的跨域和环境隔离问题。4.1 useFetch通用数据获取推荐useFetch是 Nuxt4 最常用的数据获取 API它会自动根据执行环境选择请求方式服务端渲染时在 Nuxt 服务端发起请求无跨域问题客户端导航时在浏览器发起请求享受 SPA 体验基础用法script setup langts // 获取文章列表 const { data, pending, error, refresh } await useFetch(/api/article/list, { query: { page: 1, limit: 10 }, // 查询参数 method: GET, // 请求方法 pick: [list, total] // 只保留需要的字段 }) // 响应式参数当 page 变化时自动重新请求 const page ref(1) const { data: articleList } await useFetch(/api/article/list, { query: { page, limit: 10 } }) /script template div v-ifpending加载中.../div div v-else-iferror加载失败{{ error.message }}/div div v-else div v-foritem in articleList.list :keyitem.id {{ item.title }} /div /div /template4.2 useServerFetch仅服务端请求useServerFetch只在服务端执行客户端永远不会执行这个请求适合以下场景需要 SEO 的首屏核心数据敏感数据请求如需要携带服务器密钥的接口避免暴露后端接口地址文章列表第一页的最佳实践script setup langts // 仅在服务端执行请求 ThinkPHP 后端接口 const { data: articleList } await useServerFetch(http://api.xxx.com/article/list, { query: { page: 1, limit: 10 } }) /script✅ 优势无跨域问题服务端之间的请求不受同源策略限制后端接口地址不会暴露给客户端首屏数据直接渲染在 HTML 中SEO 效果最佳4.3 $fetch手动发起请求$fetch是 Nuxt4 内置的轻量级 HTTP 客户端基于ofetch实现支持服务端和客户端环境。适合在事件处理函数中发起请求script setup langts // 分页请求客户端执行直连 ThinkPHP 后端 const getPageData async (targetPage: number) { const res await $fetch(http://api.xxx.com/article/list, { query: { page: targetPage, limit: 10 } }) articleList.value res.list page.value targetPage } /script五、状态管理与鉴权内置 API 解决所有问题Nuxt4 内置了useState和useCookie两个核心 API无需引入第三方状态管理库如 Pinia即可实现跨组件状态共享和 SSR 友好的 Token 鉴权。5.1 useState跨组件状态共享useState是 Nuxt4 内置的状态管理 API支持 SSR状态会在服务端和客户端之间自动同步。全局用户状态管理创建app/composables/useAuth.ts// app/composables/useAuth.tsinterfaceUser{id:numberusername:stringavatar:string}exportconstuseAuth(){// 定义全局状态key 为唯一标识constuseruseStateUser|null(user,()null)constisLoggedIncomputed(()!!user.value)// 登录方法constloginasync(username:string,password:string){constresawait$fetch(http://api.xxx.com/user/login,{method:POST,body:{username,password}})user.valueres.userreturnres}// 退出登录方法constlogoutasync(){await$fetch(http://api.xxx.com/user/logout,{method:POST})user.valuenull}return{user,isLoggedIn,login,logout}}在任何组件中直接使用script setup langts const { user, isLoggedIn, logout } useAuth() /script template div v-ifisLoggedIn 欢迎{{ user.username }} button clicklogout退出登录/button /div div v-else NuxtLink to/login登录/NuxtLink /div /template5.2 useCookieSSR 友好的 Cookie 操作useCookie是 Nuxt4 内置的 Cookie 操作 API支持服务端和客户端环境完美解决 SSR 场景下的 Token 鉴权问题。Token 双存储方案服务端客户端通用// app/composables/useAuth.tsexportconstuseAuth(){constuseruseStateUser|null(user,()null)constisLoggedIncomputed(()!!user.value)// 使用 useCookie 存储 Token服务端和客户端都能读取consttokenuseCookiestring(token,{maxAge:7*24*60*60,// 7 天有效期sameSite:lax,secure:process.env.NODE_ENVproduction// 生产环境使用 HTTPS})// 登录方法constloginasync(username:string,password:string){constresawait$fetch(http://api.xxx.com/user/login,{method:POST,body:{username,password}})user.valueres.user token.valueres.token// 存储 Token 到 Cookiereturnres}// 退出登录方法constlogoutasync(){await$fetch(http://api.xxx.com/user/logout,{method:POST})user.valuenulltoken.valuenull// 清除 Cookie 中的 Token}return{user,isLoggedIn,login,logout,token}}5.3 全局请求拦截器自动携带 Token创建app/plugins/axios.ts配置全局请求拦截器自动在请求头中携带 Token// app/plugins/axios.tsexportdefaultdefineNuxtPlugin((){const{token}useAuth()// 配置全局 $fetch 拦截器globalThis.$fetch$fetch.create({onRequest({options}){// 自动携带 Tokenif(token.value){options.headers{...options.headers,Authorization:Bearer${token.value}}}},onResponseError({response}){// 处理 Token 过期if(response.status401){const{logout}useAuth()logout()navigateTo(/login)}}})})六、实战项目Nuxt4 ThinkPHP 文章系统现在我们将结合 ThinkPHP 后端实现一个完整的文章系统包括文章列表、文章详情、登录、点赞、评论等功能。6.1 文章列表页首屏服务端渲染 客户端分页!-- app/pages/article/list.vue -- script setup langts const route useRoute() const page ref(Number(route.query.page) || 1) // 第一页服务端请求SEO 友好 const { data: initialData } await useServerFetch(http://api.xxx.com/article/list, { query: { page: page.value, limit: 10 } }) // 本地状态用于客户端分页更新 const articleList ref(initialData.list) const total ref(initialData.total) // 分页请求客户端执行无刷新更新 const getPageData async (targetPage: number) { const res await $fetch(http://api.xxx.com/article/list, { query: { page: targetPage, limit: 10 } }) articleList.value res.list total.value res.total page.value targetPage // 更新 URL 参数支持浏览器前进后退 navigateTo({ query: { page: targetPage } }, { replace: true }) } /script template div classarticle-list h1文章列表/h1 !-- 静态内容文章列表 -- div v-foritem in articleList :keyitem.id classarticle-item NuxtLink :to/article/${item.id} h2{{ item.title }}/h2 /NuxtLink p{{ item.description }}/p div classmeta span{{ item.createTime }}/span LikeButton :article-iditem.id :like-countitem.likeCount / /div /div !-- 交互组件分页 -- ClientOnly Pagination :totaltotal :pagepage :page-size10 changegetPageData / /ClientOnly /div /template6.2 文章详情页服务端渲染完整内容!-- app/pages/article/[id].vue -- script setup langts const route useRoute() const articleId route.params.id // 服务端请求文章详情SEO 友好 const { data: article } await useServerFetch(http://api.xxx.com/article/${articleId}) // 客户端请求评论列表 const commentList ref([]) onMounted(async () { const res await $fetch(http://api.xxx.com/comment/list, { query: { articleId } }) commentList.value res.list }) /script template div classarticle-detail h1{{ article.title }}/h1 div classmeta span作者{{ article.author }}/span span发布时间{{ article.createTime }}/span /div div classcontent v-htmlarticle.content/div !-- 评论区客户端渲染 -- ClientOnly CommentSection :article-idarticleId :comment-listcommentList / /ClientOnly /div /template6.3 登录页面Token 双存储!-- app/pages/login.vue -- script setup langts const { login } useAuth() const router useRouter() const username ref() const password ref() const loading ref(false) const handleLogin async () { if (!username.value || !password.value) { alert(请输入账号和密码) return } loading.value true try { await login(username.value, password.value) router.push(/article/list) } catch (err) { alert(登录失败 err.message) } finally { loading.value false } } /script template div classlogin h1登录/h1 input v-modelusername placeholder账号 / input v-modelpassword typepassword placeholder密码 / button clickhandleLogin :disabledloading {{ loading ? 登录中... : 登录 }} /button /div /template七、水合优化与避坑指南虽然 Nuxt4 解决了大部分水合问题但在实际开发中仍有一些需要注意的地方。7.1 延迟水合策略Nuxt4 提供了多种延迟水合策略可以进一步优化性能hydrate-on-visible组件进入视口时再水合hydrate-on-interaction用户与组件交互时再水合hydrate-never永远不水合纯静态组件template !-- 评论区进入视口时再水合 -- CommentSection hydrate-on-visible :article-idid / !-- 点击按钮时再水合 -- ShareButton hydrate-on-interaction :article-idid / !-- 纯静态组件永远不水合 -- ArticleContent hydrate-never :contentarticle.content / /template7.2 常见坑点与解决方案浏览器 API 只能在客户端使用错误直接在script setup中使用window、document正确使用onMounted钩子或process.client判断script setup langts const screenWidth ref(0) onMounted(() { screenWidth.value window.innerWidth }) /scriptv-for 必须使用唯一稳定的 key错误使用index作为 key正确使用业务唯一 ID如item.iddiv v-foritem in articleList :keyitem.id {{ item.title }} /div使用 ClientOnly 包裹客户端专属组件对于依赖浏览器环境的组件如富文本编辑器、图表组件使用ClientOnly包裹ClientOnly Editor v-modelcontent / /ClientOnly八、生产环境部署多环境部署方案Nuxt4 支持多种部署方式你可以根据项目需求选择最合适的方案。8.1 Node.js 服务器部署推荐 SSR 项目本地构建项目npmrun build构建完成后会生成.output目录这是一个自包含的可执行文件。上传.output目录到服务器使用 PM2 启动应用# 全局安装 PM2npminstall-gpm2# 启动应用pm2 start .output/server/index.mjs--namenuxt-app# 设置开机自启pm2 startup pm2 save配置 Nginx 反向代理server { listen 80; server_name shturl.cc/VLuoi; location / { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }8.2 静态站点部署SSG如果你的网站内容不经常变化可以使用静态生成模式npmrun generate构建完成后会生成.output/public目录里面是纯静态 HTML 文件可以直接部署到任何静态托管服务如 Vercel、Netlify、阿里云 OSS。8.3 与 ThinkPHP 同服务器部署将 Nuxt 应用和 ThinkPHP 应用部署在同一台服务器上通过 Nginx 配置路由区分server { listen 80; server_name shturl.cc/VLuoi; # 所有 /api 开头的请求转发到 ThinkPHP location /api { proxy_pass http://localhost:8080; # ThinkPHP 运行地址 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 其他请求转发到 Nuxt location / { proxy_pass http://localhost:3000; # Nuxt 运行地址 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }九、最佳实践与总结9.1 Nuxt4 开发最佳实践遵循岛屿式水合原则纯展示内容直接写在页面中交互逻辑封装成独立组件首屏核心数据用 useServerFetch保证 SEO 和首屏加载速度交互数据用客户端请求减轻服务端压力提升用户体验使用内置 API优先使用useState、useCookie避免引入第三方库合理使用延迟水合对于非首屏交互组件使用延迟水合策略开启 TypeScriptNuxt4 对 TypeScript 有完美支持能显著提升代码质量和开发效率9.2 总结Nuxt4 是目前最成熟、最高效的 Vue SSR 全栈框架它彻底解决了传统 SSR 开发的所有痛点。通过本文的学习你已经掌握了 Nuxt4 的核心概念、实战技巧和最佳实践能够独立开发高性能的 SSR 应用。Nuxt4 ThinkPHP 是一套非常优秀的技术组合Nuxt4 负责前端渲染和用户体验ThinkPHP 负责后端业务逻辑和数据处理两者完美互补兼顾了开发效率、性能和 SEO。无论是个人博客、企业官网还是电商平台这套技术栈都能轻松应对。未来Nuxt 还会继续朝着「全栈开发操作系统」的方向演进提供更多开箱即用的能力让开发者能够更加专注于业务逻辑的实现。现在就开始你的 Nuxt4 开发之旅吧