掘金文章归档 · 第 24 篇 Git / 工程化 系列 3/8 前端部署

解决小项目中的频繁部署

无 Jenkins / 无流水线,npm run build:deploy 一键更新服务器资源

原文作者:随意_(掘金) | juejin.cn/post/7527208802560557066 | 发布于 2025-07-16 | 阅读 708 · 约 2 分钟

0背景:小项目部署的痛点

对于一些演示项目,或者公司内部没有流水线的情况下,前端项目部署需要频繁 ssh unzip 等操作极其麻烦,或者使用 xftp 等传输工具,也需要频繁打开客户端、频繁上传拖动打包文件。

📌 目标:在没有流水线的情况下,直接执行 npm run build:deploy(或 npm run build 后再 npm run deploy)就直接更新服务器资源。适用于 nginx 部署的前端服务,每次部署完文件不需要 reload 或重启镜像的情况。

1基础实现:scp2 一键上传

1.1 安装依赖

npm i scp2 -S

1.2 package.json 中增加命令

"deploy": "node deploy.js",
"build:deploy": "npm run build && npm run deploy",

1.3 根目录增加 deploy.js

需要修改的就是服务器的信息以及部署的目录:

import scpClient from 'scp2'
import path from 'path'
import fs from 'fs'

// 服务器配置
const serverConfig = {
  host: '172.11.111.222',    // 服务器IP
  port: 111,                 // 端口
  username: 'root',          // 用户名
  password: '******',        // 密码
  path: '/aaa/bb'            // 部署路径
}

const localDistPath = path.join(process.cwd(), './dist')

if (!fs.existsSync(localDistPath)) {
  console.error('错误:dist目录不存在,请先执行构建命令')
  process.exit(1)
}

console.log(`开始上传dist文件夹到 ${serverConfig.host}:${serverConfig.path}...`)

scpClient.scp(
  localDistPath,
  serverConfig,
  (err) => {
    if (err) {
      console.error('上传失败:', err)
      process.exit(1)
    } else {
      console.log('✅ dist文件夹已成功上传到服务器!')
      process.exit(0)
    }
  }
)

2更安全的 SFTP 方案

使用 ssh2-sftp-client 替代 scp2,SFTP 协议更安全可控:

import Client from 'ssh2-sftp-client'
import path from 'path'
import fs from 'fs'

// 服务器配置
const serverConfig = {
  host: '172.11.111.222',    // 服务器IP
  port: 111,                 // 端口
  username: 'root',          // 用户名
  password: '******',        // 密码
  path: '/aaa/bb'            // 部署路径
}

const localDistPath = path.join(process.cwd(), './dist')

if (!fs.existsSync(localDistPath)) {
  console.error('错误:dist目录不存在,请先执行构建命令')
  process.exit(1)
}

console.log(`开始上传 dist 文件夹到 ${serverConfig.host}:${serverConfig.path}...`)

const sftp = new Client()

sftp.connect({
  host: serverConfig.host,
  port: serverConfig.port,
  username: serverConfig.username,
  password: serverConfig.password
})
  .then(() => {
    return sftp.uploadDir(localDistPath, serverConfig.path)
  })
  .then(() => {
    console.log('✅ dist文件夹已成功上传到服务器!')
    process.exit(0)
  })
  .catch((err) => {
    console.error('上传失败:', err)
    process.exit(1)
  })
  .finally(() => {
    sftp.end() // 关闭SFTP连接
  })
⚠️ 上面的写法把服务器信息写死在代码里,不推荐。服务器信息不应该写死,且不应该传递到仓库中,可以自己创建环境变量写入,并忽略提交。

3安全优化:环境变量 + 一键执行

3.1 根目录创建 .env.deploy 环境配置文件

# .env.deploy
VITE_SFTP_HOST=172.11.111.222
VITE_SFTP_PORT=111
VITE_SFTP_USERNAME=root
VITE_SFTP_PASSWORD=******
VITE_SFTP_REMOTE_PATH=/aaa/bb

3.2 package.json 做更改

"build:deploy": "vite build && node --env-file=.env.deploy deploy.js",
"deploy": "node --env-file=.env.deploy deploy.js",

3.3 .gitignore 修改

# 增加 .env.deploy 忽略提交
.env.deploy

3.4 deploy.js 从环境变量读取配置

import Client from 'ssh2-sftp-client'
import path from 'path'
import fs from 'fs'

// 从环境变量获取配置
const serverConfig = {
  host: process.env.VITE_SFTP_HOST,
  port: parseInt(process.env.VITE_SFTP_PORT || '22'),
  username: process.env.VITE_SFTP_USERNAME,
  password: process.env.VITE_SFTP_PASSWORD,
  path: process.env.VITE_SFTP_REMOTE_PATH
}

const localDistPath = path.join(process.cwd(), './dist')

if (!fs.existsSync(localDistPath)) {
  console.error('错误:dist目录不存在,请先执行构建命令')
  process.exit(1)
}

console.log(`开始上传 dist 文件夹到 ${serverConfig.host}:${serverConfig.path}...`)

const sftp = new Client()

sftp.connect({
  host: serverConfig.host,
  port: serverConfig.port,
  username: serverConfig.username,
  password: serverConfig.password
})
  .then(() => {
    return sftp.uploadDir(localDistPath, serverConfig.path)
  })
  .then(() => {
    console.log('✅ dist文件夹已成功上传到服务器!')
    process.exit(0)
  })
  .catch((err) => {
    console.error('上传失败:', err)
    process.exit(1)
  })
  .finally(() => {
    sftp.end() // 关闭SFTP连接
  })

执行

1. 完整流程:

  1. 先运行 vite build 构建项目;
  2. 如果构建成功,自动运行部署脚本;
  3. 部署脚本会读取 .env.deploy 中的敏感信息(服务器账号等);
  4. 将 dist 目录上传到服务器。

2. 也可直接一键式部署:

npm run build:deploy
✅ 这样小项目在没有 Jenkins、没有流水线的情况下,也可以一条命令完成构建 + 部署,省去反复 ssh / xftp 的繁琐操作。