Nuxt在使用过程中的一些小总结
最近用Nuxt4做了个服务端渲染的项目,从本地开发到服务器部署,一路顺顺利利的同时也踩了不少针对性的小坑,最典型的就是本地、服务器IP访问都完全正常,一换成域名访问就偶尔报500,折腾了一阵子总算把所有问题都理顺了。Nuxt4对比之前的版本其实优化了不少,但也有一些自己的特性和小细节需要注意,今天就用大白话写写这段时间的使用心得、踩过的坑,还有生产环境必备的Nginx和PM2完整配置,都是实打实的实战经验,没有花里胡哨的内容,希望能给同样在用Nuxt4的朋友一点参考。
先简单说下我的技术栈:Nuxt4 + Pinia 做状态管理,用的Nuxt4自带的useFetch请求接口,后端是Node.js的接口服务,服务器上用PM2守护Nuxt4服务进程,Nginx做域名解析和反向代理。整体开发体验还是很不错的,就是部署阶段的几个细节没注意到,踩了些没必要的坑。
一、第一个大坑,也是最致命的:URL端口配置不当,直接触发500报错
这个问题应该是我这次遇到的核心问题,也是最让人摸不着头脑的,纯纯是Nuxt4的解析特性,不是代码写错了。
我在项目的.env.production生产环境变量里,配置接口的基础地址时,想着HTTP协议的端口是80,就顺手把地址写成了NUXT_PUBLIC_API_BASE_URL=http://xxx.com:80/xxx/api/public。本地开发跑npm run dev的时候一点问题没有,接口请求、数据渲染都正常,打包部署到服务器后,打开PM2的日志一看直接懵了——日志里打印出来的接口地址居然变成了http://xxx.com/:80/xxx/api/public,域名后面多了个斜杠,把:80直接解析成URL路径的一部分了!
这就直接导致接口请求的地址完全无效,后端根本收不到请求,接口返回值自然就是undefined,后面代码里再去取xxx.data.result,瞬间就报Cannot read properties of undefined (reading 'data')的错,页面直接500卡死。更巧的是,我用服务器IP访问的时候,配置里没写:80,地址解析正常,所以IP访问啥事没有,一换域名就崩,这种反差真的太磨人了。
这里划重点:HTTP协议的默认端口就是80,HTTPS的默认端口是443,这两个端口在配置URL的时候,永远不要手动写上去!
Nuxt4在服务端渲染(SSR)的模式下,对带默认端口的URL解析有个小特性,手动写:80大概率会被解析成路径,不是框架的bug,就是这么个需要注意的点。而且不光是Nuxt4,很多前端框架在SSR模式下都有这个情况,记住就行,省的踩坑。
解决办法也超级简单:直接把.env文件里的:80删掉,改成NUXT_PUBLIC_API_BASE_URL=http://xxx.com/xxx/api/public就完事,解析立刻恢复正常,这一个小改动,直接解决了核心的500问题。
二、接口请求的必修课:任何请求都要加容错处理,这不是废话是保命
这个点其实不算Nuxt4专属,是前端开发的通用准则,但在Nuxt4的SSR模式下,这个问题的影响会被无限放大,我也因为偷懒没加容错,踩了这个坑。
我的项目里,在Pinia的store里写了个获取菜单数据的接口请求方法,一开始的代码写的很简洁,就是请求接口、拿数据、赋值,代码大概是这样:
asyncfetch(){constconfig=useRuntimeConfig();const{data:menuData}=awaituseFetch(`${config.public.apiBaseUrl}/column/getColumnsByTree`,{server:true});this.menus=menuData.value.data.result;// 存本地缓存if(import.meta.client){localStorage.setItem("menus",JSON.stringify(this.menus));}}本地测试的时候接口一直正常返回数据,觉得这么写没问题,结果部署后因为上面的URL解析错误,menuData.value直接变成了undefined,一行menuData.value.data.result直接让页面崩了。
现在回头看,真的是太偷懒了。Nuxt4是SSR框架,有服务端请求和客户端请求两个阶段,服务端请求的时候,网络波动、接口地址错误、后端临时挂了,这些情况都有可能发生,而且服务端环境里没有浏览器的容错机制,一旦返回undefined,直接就是致命错误。
所以在Nuxt4里写接口请求,try/catch + 可选链操作符(?.) + 兜底值这三件套,真的是缺一不可,哪怕是再简单的请求都要加上。改完之后的代码是这样的,很简单,但能保证项目绝对不会因为接口问题崩掉:
asyncfetch(){constconfig=useRuntimeConfig();try{const{data:menuData}=awaituseFetch(`${config.public.apiBaseUrl}/column/getColumnsByTree`,{server:true,timeout:5000// 加个超时,防止请求卡死});// 多层可选链判空,再加个空数组兜底,永远不会报undefined的错this.menus=menuData.value?.data?.result||[];if(import.meta.client){localStorage.setItem("menus",JSON.stringify(this.menus));}}catch(err){console.error('菜单数据请求失败:',err);this.menus=[];// 失败了也给个空数组,页面正常渲染}}改完之后,就算接口请求失败,页面也只是没有数据,不会直接500报错,用户体验和项目稳定性都提升太多了。
三、Nuxt4 那些容易搞混的小细节,都是日常开发的高频点
这里说几个我自己踩过的、也是大家问的比较多的小细节,都是Nuxt4的基础特性,但是没注意到的话,很容易出问题,而且这些细节,IDE里都会有对应的提示,很实用。
✅ 关于 import.meta.server / import.meta.client,划重点:这个写法是最新的,完全没问题!
很多人会纠结,Nuxt里到底用import.meta.server还是process.server来区分服务端和客户端,我自己的IDEA里,输入process.server的时候,直接会提示这个写法已过时,而import.meta.server和import.meta.client是Nuxt4官方推荐的最新写法,完全是当前的标准写法,不存在兼容性问题,也不会偶发失效!
我自己全程用的都是import.meta.server,在nuxt.config.ts里这么配置接口地址,一点问题都没有:
runtimeConfig:{public:{apiBaseUrl:import.meta.server?'http://127.0.0.1:7001/api/public'// 服务端渲染时,直接请求本地后端接口,无跨域:process.env.NUXT_PUBLIC_API_BASE_URL,// 客户端运行时,走域名的接口地址},},这个配置的逻辑很简单:服务端渲染阶段,直接请求服务器本地的后端接口,不走公网,没有跨域问题,速度还快;客户端阶段,用户操作页面的时候,走配置的域名接口,完美适配Nginx的转发规则。这个写法我用到现在,生产环境跑的稳稳的,大家放心用就行。
✅ useRuntimeConfig() 是读取配置的唯一正确方式
Nuxt4里,不管是nuxt.config.ts里的runtimeConfig配置,还是.env文件里的环境变量,都必须通过useRuntimeConfig()这个组合式API来读取,不能直接用process.env.xxx读取,也不能用Nuxt2里的this.$config,这是Nuxt4的强制规范。
正确的读取方式,在任何页面、组件、store里都能用:
const{public:{apiBaseUrl}}=useRuntimeConfig();这个细节很重要,写错了的话,就读取不到配置的接口地址,直接就会出问题。
✅ localStorage / sessionStorage 只能在客户端执行
这个是SSR框架的通用规则,但是Nuxt4里一定要注意:服务端渲染阶段,服务器环境里没有window对象,也没有localStorage,如果你在代码里直接写localStorage.setItem(),服务端渲染的时候会直接报错。
解决办法就是用import.meta.client做判断,只有在客户端环境下,才执行本地存储的操作,就像我上面的代码那样,这个细节很基础,但是忘一次就会报一次错,记牢就行。
四、重中之重!Nuxt4 生产环境必备:PM2 完整配置(直接复制可用)
Nuxt4项目部署到服务器,一定要用PM2来守护进程,绝对不能直接用npm run start启动,一旦断开终端连接,服务就会停掉,PM2能保证项目一直运行,还能自动重启、查看日志、监控进程状态,是Node系项目部署的标配,也是我这次排查500报错的核心工具——所有的报错日志,都能在PM2里看到。
✅ 第一步:创建PM2的配置文件(推荐,永久生效,一键启动)
在你的Nuxt4项目根目录下,新建一个ecosystem.config.js文件,这是PM2的标准配置文件,直接复制下面的配置就行,我加了详细注释,根据自己的项目改改名字和路径就好:
module.exports={apps:[{name:'nuxt4-project',// 你的项目名称,随便起,方便识别script:'./.output/server/index.mjs',// Nuxt4打包后的启动文件,固定路径,不用改exec_mode:'cluster',// 集群模式,多核服务器推荐开启,性能更好instances:'max',// 开启所有CPU核心,充分利用服务器资源autorestart:true,// 服务崩溃时自动重启,必开watch:false,// 生产环境关闭监听,防止代码改动导致服务重启max_memory_restart:'1G',// 内存占用超过1G时自动重启,防止内存泄漏// 日志配置,重中之重!所有报错都在日志里log_date_format:"YYYY-MM-DD HH:mm:ss",// 日志带时间戳,方便排查问题error_file:"./logs/nuxt-error.log",// 错误日志存放路径out_file:"./logs/nuxt-out.log",// 正常日志存放路径merge_logs:true,// 合并日志文件,避免生成太多日志log_level:"warn",// 日志级别,只打印警告和错误,日志文件不会太大// 环境变量env:{NODE_ENV:'production',NUXT_APP_ENV:'production'}}]}✅ 第二步:PM2 常用命令(日常运维够用,记这几个就行)
所有命令都在项目根目录执行,简单好记,这些是我每天都在用的,没有多余的:
# 1. 启动项目(根据配置文件启动)pm2 start ecosystem.config.js# 2. 重启项目(改了配置、重新打包后,必执行)pm2 restart nuxt4-project# nuxt4-project是你配置文件里的name# 3. 停止项目pm2 stop nuxt4-project# 4. 查看实时错误日志(排查500、接口报错的核心命令,必用!)pm2 logs nuxt4-project--err# 5. 查看所有PM2进程状态pm2 list# 6. 清空所有日志(日志文件太大时执行)pm2 flush✅ 关键提醒
Nuxt4的打包产物在.output目录下,启动文件固定是./.output/server/index.mjs,这个路径不要改,改了就启动不了服务了。
五、域名访问的核心!Nuxt4 + 后端接口 完整 Nginx 配置(直接复制可用,解决所有域名问题)
如果说PM2是保证项目能运行,那Nginx就是保证项目能通过域名正常访问,也是解决IP访问正常、域名访问异常的核心配置。我的项目里,Nginx主要做两件事:1、把域名的请求转发给Nuxt4的3000端口;2、把接口的请求转发给后端的7001端口;同时还能解决跨域、/favicon.ico缺失、无效爬虫请求等问题,这份配置是我根据实际项目调整后的完整版,直接复制粘贴,改改域名和端口就能用,没有多余的配置,非常实用。
✅ 完整Nginx配置(重点注释都加上了)
找到你的Nginx配置文件(宝塔面板的话在「网站」-「配置文件」里,原生Nginx在/usr/local/nginx/conf/nginx.conf),新增一个server块即可:
server { listen 80; # 这里写你的域名 + 服务器IP,多个用空格隔开 server_name xxx.com www.xxx.com 121.xxx.xxx.xxx; # 编码格式,防止中文乱码 charset utf-8; # ========== 核心1:把域名的所有请求,转发给Nuxt4的3000端口 ========== location / { proxy_pass http://127.0.0.1:3000; # Nuxt4的运行端口,默认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; proxy_http_version 1.1; proxy_set_header Connection ""; proxy_cache_bypass $http_upgrade; } # ========== 核心2:把后端接口的请求,转发给后端服务的端口(我的是7001) ========== # 这里的 /OfficialWebsiteServer/ 是你的接口前缀,根据自己的后端配置改 location ^~ /OfficialWebsiteServer/ { proxy_pass http://127.0.0.1:7001/; # 后端接口的运行端口 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 解决跨域问题,必加 add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS'; add_header Access-Control-Allow-Headers 'Content-Type, Authorization'; } # ========== 解决日志里的 favicon.ico 缺失警告 ========== # 不用特意放favicon.ico文件,直接配置这个就行,日志里不会再报这个错 location /favicon.ico { log_not_found off; access_log off; } # ========== 过滤掉爬虫的无效请求(比如日志里的 /1234.php 这类) ========== # 这类请求都是外网爬虫试探,直接拦截,日志更清爽 location ~* \.(php|jsp|asp)$ { return 404; } # 静态资源缓存,提升访问速度 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 7d; add_header Cache-Control "public, max-age=604800"; access_log off; } }✅ Nginx 配置后必执行的命令
改完配置后,一定要执行这两个命令,让配置生效,同时检查配置是否有语法错误:
# 检查Nginx配置是否有语法错误nginx-t# 重载Nginx配置(不重启服务,无缝生效)nginx-sreload这份配置解决了我日志里所有的小警告,也完美适配了Nuxt4和后端接口的转发,域名访问的所有问题都靠它解决了。
六、Nuxt4 部署上线的正确流程,一步都不能少!
最后说一下部署的完整流程,这个流程看起来简单,但是少一步都可能导致配置不生效、项目报错,我自己因为偷懒跳过了「清除缓存」和「重新打包」,踩过两次坑,现在每次部署都严格按这个步骤来,一次成功:
# 1. 进入你的Nuxt4项目根目录cd/你的项目路径/zjweb# 2. 清除Nuxt4的缓存(重中之重!清除旧的配置和打包产物)npx nuxi clean# 3. 重新打包生产环境代码(改了任何配置、代码,都必须重新打包)npmrun build# 4. 重启PM2服务,加载新的打包产物pm2 restart nuxt4-project# 5. 查看PM2错误日志,确认没有报错pm2 logs nuxt4-project--err划重点:Nuxt4的环境变量和runtimeConfig配置,都是打包时注入的,不是运行时动态读取的!如果你改了.env文件或者nuxt.config.ts,只重启PM2是没用的,必须重新打包,否则还是用的旧配置,这个是新手最容易踩的坑,一定要记住。
最后,随手记的几个Nuxt4实战小技巧,都是干货
- 本地开发的时候,用
nuxt dev --dotenv .env.development指定开发环境的配置文件,生产打包用nuxt build --dotenv .env.production,开发和生产环境彻底分离,不会混配置。 - Nuxt4的devtools真的很好用,开启后能直接看接口请求、状态管理、路由,开发效率提升很多,配置里直接写
devtools: { enabled: true }就行。 - 用useFetch请求接口的时候,加上
server: true可以让接口在服务端渲染阶段就请求完成,页面加载速度更快,SEO也更好。 - 生产环境的日志是排查问题的唯一途径,遇到500报错,先看PM2的错误日志,不要瞎猜问题,日志里都会写清楚报错的文件、行数、原因。
写在最后
其实这次用Nuxt4开发项目,整体体验还是很不错的,框架的优化很明显,开发效率也高。这次踩的所有坑,说到底都不是框架本身的问题,而是自己对一些细节的理解不到位,还有就是部署阶段的配置没做足。
Nuxt4作为新一代的SSR框架,特性很多,但只要把核心的配置、部署、容错这几点做好,项目跑起来还是非常稳定的。IP访问正常、域名访问报错,这个问题看似头疼,其实就是几个小细节的叠加,解决了之后发现,原来都是纸老虎。
希望这篇总结能帮到正在用Nuxt4的朋友,少踩点坑,多写点好代码。技术这条路,无非就是踩坑、总结、再前进,共勉~