一、本篇目标

前六篇完成了后端和基础环境:

  • 第 1 篇:升级决策
  • 第 2 篇:环境准备与数据盘
  • 第 3 篇:Redis 7.4 源码编译
  • 第 4 篇:Nginx 1.28 源码编译
  • 第 5 篇:PostgreSQL 17 源码编译 + pgvector + SQL 转换
  • 第 6 篇:JeecgBoot 3.9.5 后端编译部署

后端已经跑在 8080 端口,本篇处理前端:

  • Node.js 22 环境搭建
  • pnpm 10 安装与前端编译
  • Nginx 部署 Vue3 静态资源
  • 反向代理后端接口
  • SPA history 路由回落
  • WebSocket 代理

最终目标:

浏览器打开 http://你的IP  ->  看到 JeecgBoot 登录页
登录 admin/123456          ->  进入主界面
刷新任意子路由             ->  不报 404

本篇的实战过程中踩了不少坑,尤其是 Node.js 和 pnpm 的版本兼容性,都在下文完整记录。

二、部署架构

JeecgBoot 前后端分离,Nginx 是唯一入口:

浏览器
  │
  ▼
Nginx :80
  ├── /              → /usr/local/jeecg/frontend  (静态文件)
  ├── /jeecgboot/    → 127.0.0.1:8080/jeecg-boot/  (API 反向代理)
  └── /websocket     → 127.0.0.1:8080/websocket    (WebSocket)

三、编译前准备

3.1 修改 .env.production

cd /usr/local/src/JeecgBoot/jeecgboot-vue3
cp .env.production .env.production.bak-$(date +%Y%m%d-%H%M%S)
sed -i 's#^VITE_GLOB_DOMAIN_URL=.*#VITE_GLOB_DOMAIN_URL=http://192.168.1.6/jeecgboot#' .env.production
grep -E 'VITE_GLOB_API_URL|VITE_GLOB_DOMAIN_URL' .env.production

期望输出:

VITE_GLOB_API_URL=/jeecgboot
VITE_GLOB_DOMAIN_URL=http://192.168.1.6/jeecgboot

注意:JeecgBoot 3.9.5 的 VITE_GLOB_DOMAIN_URL 会在运行时通过 _app.config.js 注入,换 IP 只需改这个文件,不必重新编译。

3.2 初始环境版本

组件版本
Node.js20.18.2(系统 rpm 安装)
pnpm12.3.4
内存93G

看起来够用,但后面会证明这两项都不行。

四、编译踩坑全记录

坑 1:Node.js 版本不足导致 rolldown 原生模块缺失

第一次执行 pnpm install && pnpm run build,报错:

Error: Cannot find native binding. npm has a bug related to optional dependencies...
cause: Error: Cannot find module '@rolldown/binding-linux-x64-gnu'

原因:Vite 8 依赖 rolldown,其原生模块要求 Node.js ≥ 20.19 或 ≥ 22.12,而当前是 20.18.2。pnpm 在解析可选依赖时跳过了不兼容的包。

解决:升级 Node.js 到 22.23.3(手动下载官方二进制):

cd /tmp
LATEST=v22.23.3
curl -fLO https://nodejs.org/dist/${LATEST}/node-${LATEST}-linux-x64.tar.xz
tar -xJf node-${LATEST}-linux-x64.tar.xz
mv /usr/local/node22 /usr/local/node22-bak-22.11.0-$(date +%Y%m%d)
mv /tmp/node-${LATEST}-linux-x64 /usr/local/node22
export PATH=/usr/local/node22/bin:$PATH
node -v   # v22.23.3

坑 2:pnpm 12 的 ERR_PNPM_IGNORED_BUILDS

安装依赖时提示:

Error: ERR_PNPM_IGNORED_BUILDS
  × installing dependencies
  ╰─▶ Ignored build scripts: @logicflow/core@2.2.4, @scarf/scarf@1.4.0, core-js@2.6.12, ...

解决:批准构建脚本:

pnpm approve-builds --all

或在 pnpm-workspace.yaml 中配置 allowBuilds。

坑 3:pnpm 12 的 minimumReleaseAge 供应链策略

安装依赖时不断报:

Error: ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION
  × installing dependencies
  ╰─▶ 4 lockfile entries failed verification:
        exsolve@1.1.3 was published at 2026-10-03T09:36:58.881Z, within the minimumReleaseAge cutoff
        ...

pnpm 11+ 默认开启 minimumReleaseAge(默认 1440 分钟),拒绝安装发布不足 24 小时的包。

解决:在项目根目录的 pnpm-workspace.yaml 中显式设置 minimumReleaseAge: 0:

supportedArchitectures:
  os:
    - linux
  cpu:
    - x64
  libc:
    - glibc

allowBuilds:
  '@logicflow/core': true
  '@scarf/scarf': true
  core-js: true
  esbuild: true
  vue-demi: true

minimumReleaseAge: 0

关键:命令行 --config.minimumReleaseAge=0 和环境变量方式在 pnpm 12 中可能被忽略,必须写进 pnpm-workspace.yaml。

坑 4:rolldown binding 始终缺失,降级 pnpm 10 并手动安装

即使 Node 升级到 22.23.3,@rolldown/binding-linux-x64-gnu 仍然没有装上。尝试 pnpm 12 多种方式无效,最终降级 pnpm 到 10.34.6:

npm install -g pnpm@10 --registry=https://registry.npmmirror.com
pnpm -v  # 10.34.6

然后手动添加 binding 包:

cd /usr/local/src/JeecgBoot/jeecgboot-vue3
pnpm add -D @rolldown/binding-linux-x64-gnu@1.2.12 --registry=https://registry.npmmirror.com

验证:

ls -la node_modules/@rolldown/
# 应出现 binding-linux-x64-gnu 软链接
find node_modules -name "*.node" -path "*rolldown*"
# 找到 rolldown-binding.linux-x64-gnu.node

坑 5:cross-env 未找到命令

编译时出现:

sh: 行 1: cross-env: 未找到命令

这是因为前面的依赖安装因 minimumReleaseAge 失败,导致 node_modules 不完整。解决 minimumReleaseAge 后,重新执行 pnpm install 即可恢复。

五、最终编译成功

确保 Node 22.23.3、pnpm 10.34.6,且 pnpm-workspace.yaml 配置正确后,执行:

cd /usr/local/src/JeecgBoot/jeecgboot-vue3
export PATH=/usr/local/node22/bin:$PATH
export NODE_OPTIONS=--max-old-space-size=8192
pnpm install
pnpm run build

编译成功输出类似:

✓ built in 1m 20s
Done in 4.4s using pnpm v10.34.6

检查产物:

du -sh dist/          # 36M
ls dist/              # _app.config.js, assets, index.html, js, ...
ls -lh dist/index.html # 17K

关键文件 _app.config.js 内容:

window.__PRODUCTION__JEECGBOOT_PRO__CONF__={
  ...
  "VITE_GLOB_API_URL":"/jeecgboot",
  "VITE_GLOB_DOMAIN_URL":"http://192.168.1.6/jeecgboot",
  ...
}

六、部署到 Nginx

6.1 拷贝静态文件

mkdir -p /usr/local/jeecg/frontend
rm -rf /usr/local/jeecg/frontend/*
cp -rf /usr/local/src/JeecgBoot/jeecgboot-vue3/dist/* /usr/local/jeecg/frontend/
chown -R jeecg:jeecg /usr/local/jeecg/frontend
chmod -R 755 /usr/local/jeecg/frontend
chmod -R o+rX /usr/local/jeecg/frontend   # 确保 nginx worker 可读

6.2 配置 Nginx vhost

创建 /usr/local/nginx/conf/vhost/jeecg.conf:

server {
    listen 80;
    server_name 192.168.1.6 localhost;

    root /usr/local/jeecg/frontend;
    index index.html;

    access_log /data/logs/nginx/jeecg-access.log;
    error_log  /data/logs/nginx/jeecg-error.log;

    location = /sw.js {
        add_header Cache-Control "no-store, no-cache, must-revalidate";
    }
    location = /manifest.webmanifest {
        add_header Cache-Control "no-store, no-cache, must-revalidate";
    }
    location = /_app.config.js {
        add_header Cache-Control "no-store, no-cache, must-revalidate";
    }

    location ~* \.(?:js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    location / {
        try_files $uri $uri/ /index.html;
    }

    location /jeecgboot/ {
        proxy_pass http://127.0.0.1:8080/jeecg-boot/;
        proxy_redirect off;
        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_connect_timeout 60s;
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }

    location /websocket {
        proxy_pass http://127.0.0.1:8080/websocket;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html { root html; }
}

在 /usr/local/nginx/conf/nginx.conf 的 http 块中补充:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

include /usr/local/nginx/conf/vhost/*.conf;

6.3 日志目录与重载

mkdir -p /data/logs/nginx
chown -R nginx:nginx /data/logs/nginx
/usr/local/nginx/sbin/nginx -t
systemctl reload nginx

七、验证

curl -sI http://192.168.1.6/ | head -3
curl -sI http://192.168.1.6/_app.config.js | head -3
curl -sI http://192.168.1.6/jeecgboot/sys/randomImage | head -3

全部返回 200。浏览器访问 http://192.168.1.6,使用 admin / 123456 登录成功。

八、遗留问题:界面显示为 3.8 风格

登录后界面仍是旧版风格,初步判断是主题配置未生效。

JeecgBoot 3.9.5 支持多种菜单风格,可在 src/settings/projectSetting.ts 中修改 menuType,然后重新编译。

此问题将在后续文章中解决。

九、总结

本次前端部署主要耗时在 Node.js 和 pnpm 的版本兼容性上,核心踩坑点:

坑关键解决
rolldown 原生模块缺失Node ≥ 20.19 / 22.12升级到 Node 22.23.3
ERR_PNPM_IGNORED_BUILDSpnpm 12 策略pnpm approve-builds --all
ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATIONpnpm 11+ 默认 1440 分钟pnpm-workspace.yaml 写 minimumReleaseAge: 0
binding 包缺失pnpm 12 可选依赖解析 bug降级到 pnpm 10.34.6 + 手动 add
cross-env 找不到依赖装不全修复 minimumReleaseAge 后重装

Nginx 配置要点:

  • SPA 路由回落:try_files $uri $uri/ /index.html
  • _app.config.js 不缓存:Cache-Control: no-store
  • WebSocket 需要 map $http_upgrade $connection_upgrade
  • 静态资源长缓存:expires 30d

最终一键编译命令:

export PATH=/usr/local/node22/bin:$PATH
export NODE_OPTIONS=--max-old-space-size=8192
cd /usr/local/src/JeecgBoot/jeecgboot-vue3
pnpm install
pnpm run build

下一篇:部署归档与一键恢复。


项目地址:

  • JeecgBoot PostgreSQL 工具链:https://gitee.com/dboru/jeecgboot-pg-tools
  • SQL 转换工具:https://gitee.com/dboru/jeecgboot-pg-converter

本系列文章:

配套文章:

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐