前端小小白,刚开始使用AI辅助编程前端项目,使用的是React框架,让AI实现功能的时候它使用到了动态路由,我目前的理解就是将一个单传的前端项目变成了全栈前后端一体的项目,导致
npm build执行完之后并没有生成期望的dist文件夹或output文件夹,而是将所有内容打包到了.next文件夹中,并且不只是单纯的路径变化,这种打包方式没有生成静态页面入口,而是打出来了一个server.js文件作为入口,连静态页面都没部署过的我,真的被难死了。还好,claude大法好,我不断提问和测试,终究还是解决了,记录一下解决方案。
部署方案:使用 standalone 模式
第一步:本地构建
在你的本地项目目录执行:
bash
npm run build
构建完成后,你会看到:
javascript
✓ Compiled successfully
✓ Linting and checking validity of types
✓ Collecting page data
✓ Generating static pages
✓ Finalizing page optimization
并且项目路径下会有.next文件夹
第二步:准备部署文件
构建成功后,需要准备以下文件:
javascript
部署包结构:
├── server.js # 来自 .next/standalone/server.js
├── package.json # 来自 .next/standalone/package.json
├── .next/
│ └── server/ # 来自 .next/server/
│ └── static/ # 来自 .next/static/
│ └── xxx # 来自 .next下其他文件
└── public/ # 来自根目录的 public/
第三步:打包上传
使用 PowerShell 脚本
创建 package-deploy.ps1:
powershell
Write-Host "正在打包部署文件..." -ForegroundColor Green
# 创建临时目录
if (Test-Path deploy-temp) { Remove-Item -Recurse -Force deploy-temp }
New-Item -ItemType Directory -Path deploy-temp | Out-Null
# 复制文件
Write-Host "复制 standalone 文件..." -ForegroundColor Yellow
Copy-Item -Path ".next/standalone/*" -Destination "deploy-temp/" -Recurse -Force
Write-Host "复制 static 文件..." -ForegroundColor Yellow
New-Item -ItemType Directory -Path "deploy-temp/.next" -Force | Out-Null
Copy-Item -Path ".next/static" -Destination "deploy-temp/.next/" -Recurse -Force
Write-Host "复制 public 文件..." -ForegroundColor Yellow
Copy-Item -Path "public" -Destination "deploy-temp/" -Recurse -Force
# 创建压缩包
Write-Host "创建 deploy.zip..." -ForegroundColor Yellow
Compress-Archive -Path "deploy-temp/*" -DestinationPath "deploy.zip" -Force
# 清理
Remove-Item -Recurse -Force deploy-temp
Write-Host "打包完成!文件: deploy.zip" -ForegroundColor Green
Write-Host "文件大小: $((Get-Item deploy.zip).Length / 1MB) MB" -ForegroundColor Cyan
运行:
bash
powershell -ExecutionPolicy Bypass -File package-deploy.ps1
第四步:服务器部署
1. 上传文件到服务器
bash
# 使用 scp 上传(Linux/Mac)
scp deploy.zip user@your-server:/path/to/app/
# 或使用 WinSCP、FileZilla 等工具
2. 在服务器上解压并运行
bash
# SSH 登录服务器
ssh user@your-server
# 进入应用目录
cd /path/to/app
# 解压文件
unzip deploy.zip
# 查看文件结构(确认正确)
ls -la
# 应该看到:server.js, package.json, .next/, public/
# 直接运行(测试)
HOSTNAME=0.0.0.0 PORT=3889 node server.js
# 或使用 PM2(生产环境推荐)
HOSTNAME=0.0.0.0 PORT=3889 pm2 start server.js --name your-app
pm2 save
pm2 startup
直接运行成功显示如下:
使用 PM2 运行成功显示如下:
成功后可以使用pm2 status查看状态: 
PM命令
- 停止并删除错误的进程
bash
pm2 stop your-app
pm2 delete your-app
- 查看 PM2 状态
shell
pm2 status
- 查看错误日志
shell
pm2 logs your-app --err --lines 50
- 查看所有日志
shell
pm2 logs wps-app --lines 50
- 查看进程详情
shell
pm2 show your-app
全自动,解放双手
按照目前的部署方式,每次都有一步步手动操作,麻烦且容易出错,所以编写一个自动化脚本,保证不出错情况下解放双手!
-
创建部署应用相关配置文件
deploy-config.jsonjson{ "server": { "host": "192.168.41.11", "user": "root", "port": 22, "deployPath": "/opt/module/wps/test3" }, "app": { "name": "your-app", "port": 3889, "pm2Name": "your-app" }, "build": { "command": "npm run build", "outputDir": ".next" } } -
打包部署脚本 该脚本是powershell脚本,在windows环境运行,
xxx.ps1,脚本执行需要依赖deploy-config.jsonshell# ==================================== # Next.js Standalone Auto Deploy Script (Windows PowerShell) # ==================================== # Stop on error $ErrorActionPreference = "Stop" # Read configuration file Write-Host "=====================================" -ForegroundColor Cyan Write-Host " Next.js Auto Deploy Script" -ForegroundColor Cyan Write-Host "=====================================" -ForegroundColor Cyan Write-Host "" if (-not (Test-Path "deploy-config.json")) { Write-Host "Error: deploy-config.json not found" -ForegroundColor Red Write-Host "Please create configuration file first" -ForegroundColor Yellow exit 1 } $config = Get-Content "deploy-config.json" | ConvertFrom-Json Write-Host "Deploy Configuration:" -ForegroundColor Green Write-Host " Server: $($config.server.user)@$($config.server.host)" -ForegroundColor White Write-Host " Path: $($config.server.deployPath)" -ForegroundColor White Write-Host " Port: $($config.app.port)" -ForegroundColor White Write-Host "" # Confirm deployment $confirm = Read-Host "Continue with deployment? (y/n)" if ($confirm -ne "y" -and $confirm -ne "Y") { Write-Host "Deployment cancelled" -ForegroundColor Yellow exit 0 } Write-Host "" # ==================================== # Step 1: Build Project # ==================================== Write-Host "=====================================" -ForegroundColor Cyan Write-Host "Step 1/5: Build Project" -ForegroundColor Cyan Write-Host "=====================================" -ForegroundColor Cyan try { Write-Host "Executing: $($config.build.command)" -ForegroundColor Yellow Invoke-Expression $config.build.command if ($LASTEXITCODE -ne 0) { throw "Build failed" } Write-Host "Build successful" -ForegroundColor Green Write-Host "" } catch { Write-Host "Build failed: $_" -ForegroundColor Red exit 1 } # ==================================== # Step 2: Package Files # ==================================== Write-Host "=====================================" -ForegroundColor Cyan Write-Host "Step 2/5: Package Files" -ForegroundColor Cyan Write-Host "=====================================" -ForegroundColor Cyan try { # Create temp directory $tempDir = "deploy-temp" if (Test-Path $tempDir) { Remove-Item -Recurse -Force $tempDir } New-Item -ItemType Directory -Path $tempDir | Out-Null # Copy standalone files Write-Host "Copying standalone files..." -ForegroundColor Yellow $standalonePath = ".next/standalone" if (-not (Test-Path $standalonePath)) { throw "Cannot find .next/standalone directory. Please ensure next.config.ts has output: 'standalone'" } Copy-Item -Path "$standalonePath/*" -Destination $tempDir -Recurse -Force # Copy static files Write-Host "Copying static files..." -ForegroundColor Yellow $staticPath = ".next/static" if (Test-Path $staticPath) { $nextDir = Join-Path $tempDir ".next" if (-not (Test-Path $nextDir)) { New-Item -ItemType Directory -Path $nextDir | Out-Null } Copy-Item -Path $staticPath -Destination $nextDir -Recurse -Force } # Copy public files Write-Host "Copying public files..." -ForegroundColor Yellow if (Test-Path "public") { Copy-Item -Path "public" -Destination $tempDir -Recurse -Force } # Create archive Write-Host "Creating deploy.zip..." -ForegroundColor Yellow $zipPath = "deploy.zip" if (Test-Path $zipPath) { Remove-Item -Force $zipPath } Compress-Archive -Path "$tempDir/*" -DestinationPath $zipPath -Force $zipSize = [math]::Round((Get-Item $zipPath).Length / 1MB, 2) Write-Host "Package successful (Size: $zipSize MB)" -ForegroundColor Green Write-Host "" # Cleanup temp directory Remove-Item -Recurse -Force $tempDir } catch { Write-Host "Package failed: $_" -ForegroundColor Red if (Test-Path $tempDir) { Remove-Item -Recurse -Force $tempDir } exit 1 } # ==================================== # Step 3: Upload to Server # ==================================== Write-Host "=====================================" -ForegroundColor Cyan Write-Host "Step 3/5: Upload to Server" -ForegroundColor Cyan Write-Host "=====================================" -ForegroundColor Cyan # Check if scp is available $scpTest = Get-Command scp -ErrorAction SilentlyContinue if (-not $scpTest) { Write-Host "Error: scp command not found. Please install OpenSSH client." -ForegroundColor Red exit 1 } $uploadSuccess = $false $maxRetries = 3 $retryCount = 0 while (-not $uploadSuccess -and $retryCount -lt $maxRetries) { try { if ($retryCount -gt 0) { Write-Host "" Write-Host "Retry attempt $retryCount of $($maxRetries - 1)..." -ForegroundColor Yellow } Write-Host "Uploading to $($config.server.host)..." -ForegroundColor Yellow $scpTarget = "$($config.server.user)@$($config.server.host):$($config.server.deployPath)/deploy.zip" # Upload file & scp -P $config.server.port deploy.zip $scpTarget if ($LASTEXITCODE -eq 0) { $uploadSuccess = $true Write-Host "Upload successful" -ForegroundColor Green Write-Host "" } else { $retryCount++ if ($retryCount -lt $maxRetries) { Write-Host "Upload failed. Please check password and try again." -ForegroundColor Yellow } } } catch { $retryCount++ if ($retryCount -lt $maxRetries) { Write-Host "Upload failed: $_" -ForegroundColor Red Write-Host "Please try again..." -ForegroundColor Yellow } } } if (-not $uploadSuccess) { Write-Host "" Write-Host "Upload failed after $maxRetries attempts" -ForegroundColor Red Write-Host "" Write-Host "Tips:" -ForegroundColor Yellow Write-Host " 1. Check if server address and port are correct" -ForegroundColor Yellow Write-Host " 2. Verify your password" -ForegroundColor Yellow Write-Host " 3. Consider setting up SSH key authentication" -ForegroundColor Yellow exit 1 } # ==================================== # Step 4: Extract Files # ==================================== Write-Host "=====================================" -ForegroundColor Cyan Write-Host "Step 4/5: Extract Files" -ForegroundColor Cyan Write-Host "=====================================" -ForegroundColor Cyan Write-Host "Extracting files on server..." -ForegroundColor Yellow $sshTarget = "$($config.server.user)@$($config.server.host)" $deployPath = $config.server.deployPath $pm2Name = $config.app.pm2Name $port = $config.app.port # Step 1: Extract files & ssh -p $config.server.port $sshTarget "cd $deployPath && unzip -o deploy.zip && rm deploy.zip" Write-Host "Files extracted" -ForegroundColor Green Write-Host "" # ==================================== # Step 5: Start Application # ==================================== Write-Host "=====================================" -ForegroundColor Cyan Write-Host "Step 5/5: Start Application" -ForegroundColor Cyan Write-Host "=====================================" -ForegroundColor Cyan Write-Host "Starting application with PM2..." -ForegroundColor Yellow # Step 2: Stop old process & ssh -p $config.server.port $sshTarget "pm2 stop $pm2Name 2>/dev/null; pm2 delete $pm2Name 2>/dev/null; true" # Step 3: Start new process with HOSTNAME=0.0.0.0 to fix IPv6 listening issue & ssh -p $config.server.port $sshTarget "cd $deployPath && HOSTNAME=0.0.0.0 PORT=$port pm2 start server.js --name $pm2Name" # Step 4: Save PM2 config & ssh -p $config.server.port $sshTarget "pm2 save" Write-Host "" Write-Host "Verifying application status..." -ForegroundColor Yellow # Verify deployment & ssh -p $config.server.port $sshTarget "pm2 list" Write-Host "" Write-Host "Application started" -ForegroundColor Green Write-Host "" # ==================================== # Complete # ==================================== Write-Host "=====================================" -ForegroundColor Green Write-Host " Deployment Successful!" -ForegroundColor Green Write-Host "=====================================" -ForegroundColor Green Write-Host "" Write-Host "Application Info:" -ForegroundColor Cyan Write-Host " URL: http://$($config.server.host):$($config.app.port)" -ForegroundColor White Write-Host " PM2 Name: $($config.app.pm2Name)" -ForegroundColor White Write-Host "" Write-Host "Common Commands:" -ForegroundColor Cyan Write-Host " View logs: ssh $($config.server.user)@$($config.server.host) 'pm2 logs $($config.app.pm2Name)'" -ForegroundColor White Write-Host " View status: ssh $($config.server.user)@$($config.server.host) 'pm2 status'" -ForegroundColor White Write-Host " Restart app: ssh $($config.server.user)@$($config.server.host) 'pm2 restart $($config.app.pm2Name)'" -ForegroundColor White Write-Host "" # Cleanup local files if (Test-Path "deploy.zip") { Remove-Item -Force "deploy.zip" } Write-Host "Press any key to exit..." $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
圆满,大功告成!