🤯 什么?!还在跪求后端大佬帮你上线项目?NodeSSH 让你前端也能玩转部署:从求人到自主的逆袭之路!

💔 每个前端都经历过的痛:上线之殇

"哥,能帮我部署下项目吗?就更新个按钮样式..."

"姐,服务器怎么重启nginx啊?能不能..."

"大佬,我代码传好了但是白屏,能帮我看看吗..."

是不是很熟悉?作为前端,我们经常遇到这样的困境:

🆘 传统部署的三大噩梦

  1. 卑微求人模式

    • 每次改个CSS都要找后端帮忙
    • 后端大佬在开会/在忙/在度假...
    • "这个简单,你自己搞搞呗"(但根本不会啊!)
  2. FTP上古神器

    • 传着传着断线了
    • 传完了发现漏了几个文件
    • 覆盖了不该覆盖的配置
  3. 服务器黑魔法

diff 复制代码
-   Linux命令像天书
diff 复制代码
-   权限问题永远搞不定
-   "sudo rm -rf /" 警告!(千万别试!)

🔑 解放方案:NodeSSH ------ 前端的部署自由卡

为什么SSH是终极解决方案?

SSH就像给你的电脑和服务器之间架了座加密大桥:

  • 直接操作服务器:再也不用求人帮忙敲命令
  • 自动化脚本:一次编写,终身受用
  • 文件传输稳定:比FTP可靠100倍
  • 全前端技术栈:用你最熟悉的JavaScript搞定

🌈 自主部署带来的美好生活

  1. 随时上线:半夜灵感来了?自己搞定!
  2. 快速迭代:小改动分分钟上线,不用排队
  3. 故障自修:页面挂了?自己就能回滚版本
  4. 技能升级:从此简历可以写上"全栈部署能力"

🌟 认识我们的"魔法杖":NodeSSH

首先隆重介绍今天的主角------node-ssh,它就像是给你的Node.js装上了一根SSH魔法杖!这个神奇的库可以让你:

  • 📡 远程连接服务器(就像在服务器门口变出个任意门)
  • 💻 执行各种Linux命令(动动键盘就能让服务器乖乖听话)
  • 📁 上传下载文件(比FTP还要方便的文件快递服务) 安装它只需要一句咒语:
js 复制代码
npm install node-ssh

🧙‍♂️ 部署脚本解析:分步拆解魔法咒语

1️⃣ 准备阶段:导入法宝

js 复制代码
import { NodeSSH } from 'node-ssh'; // 主角登场
import { deployConfig } from './config.js'; // 服务器钥匙
import path from 'path'; // 路径导航仪

// 获取当前法术书位置(兼容ES模块的现代魔法)
const __dirname = path.dirname(new URL(import.meta.url).pathname);

2️⃣ 连接服务器:打开魔法传送门

js 复制代码
const ssh = new NodeSSH(); // 掏出我们的魔法杖

try {
  // 念出连接咒语(使用config.js中的配置)
  await ssh.connect(deployConfig);
  console.log('🎉 SSH连接成功!服务器说:"欢迎光临~"');
  
  // ...后续操作
} catch (err) {
  console.error('💥 啊哦!连接失败:', err);
}

3️⃣ 定位dist目录:寻找"宝藏地图"

js 复制代码
// 像海盗寻宝一样找到dist目录
let localDistPath = path.join(path.dirname(__dirname), 'dist');
// 处理Windows路径的小脾气(把反斜杠变成正斜杠)
localDistPath = localDistPath.replace(/^\\+/, '');

console.log(`🔍 找到宝藏位置:${localDistPath}`);

🗺️ 路径定位原理

假设你的项目结构是这样的:

js 复制代码
my-project/
├── deploy/       ← 我们的脚本在这里
│   └── deploy.js
└── dist/         ← 我们要找的"宝藏"

4️⃣ 上传文件:施展文件传送术

js 复制代码
const remoteDistPath = '/usr/share/nginx/html'; // 服务器上的"宝箱"

console.log(`🚀 开始传送:${localDistPath} → 服务器:${remoteDistPath}`);

// 最强大的咒语------putDirectory!
const uploadResult = await ssh.putDirectory(localDistPath, remoteDistPath, {
  recursive: true,  // 连子文件夹一起传送
  concurrency: 10,  // 同时传送10个文件(效率MAX!)
  tick: (localPath) => { // 进度播报员
    console.log(`📤 正在传送:${path.basename(localPath)}`);
  }
});

console.log(uploadResult ? '🎊 传送成功!' : '😱 传送失败!');

5️⃣ 收尾工作:打扫魔法现场

js 复制代码
// 可选:重启nginx让新版本生效
const restartResult = await ssh.execCommand('sudo systemctl restart nginx');
console.log(restartResult.stderr || '🔄 Nginx重启成功!');

// 最后记得关闭传送门哦!
ssh.dispose();
console.log('🏁 部署完成!可以打开浏览器验收啦~');

最后贴一份已经写完的基础代码,如果有其他需求增加可以自己编辑

目录结构

js 复制代码
my-project/
├── script/       ← 我们的脚本在这里
│   └── node_ssh.js
│   └──config.js
└── dist/         ← 我们要找的"宝藏"
└──src/
└──ssh_tools.jsonc

node_ssh文件

js 复制代码
//scripts/node_ssh
import { NodeSSH } from 'node-ssh';
import { deployConfig } from './config.js';
import path from 'path';


const __dirname = path.dirname(new URL(import.meta.url).pathname);

async function deploy() {
  const ssh = new NodeSSH();

  try {
   
    await ssh.connect(deployConfig);
    console.log('SSH 连接成功!');

   
    const result = await ssh.execCommand('ls');
    console.log('命令执行结果:', result);

   
    const deployScript = `
      cd /usr/share/nginx/html;
    `;
    const deployResult = await ssh.execCommand(deployScript);
    console.log('部署脚本执行结果:', deployResult);

   
    let localDistPath = path.join(path.dirname(__dirname), 'dist'); 
  
    

    localDistPath = localDistPath.replace(/^\\+/, '');
   
    
    const remoteDistPath = '/usr/share/nginx/html';     

    // 上传 dist 文件夹
    const uploadResult = await ssh.putDirectory(localDistPath, remoteDistPath, {
      recursive: true,  
      concurrency: 10,  
    });

    console.log('上传结果:', uploadResult);

    // const restartResult = await ssh.execCommand('sudo systemctl restart nginx');
    // console.log('Nginx 重启结果:', restartResult);

  } catch (err) {
    console.error('SSH 连接或命令执行出错:', err);
  }
}

deploy();

config文件

js 复制代码
//srcipt/config.js
export const deployConfig = {
    host: '服务器公网地址',
    username: '账号',
    password: '服务器密码'
  }
  

ssh_tools.jsonc配置

js 复制代码
{
	"test": {
		"host": "",
		"port": ,
		"username": "",
		"password": "",
		"proxy": false,
		"upload_on_save": false,
		"watch": true,
		"submit_git_before_upload": false,
		"submit_git_msg": "",
		"compress": false,
		"remote_unpacked": true,
		"delete_remote_compress": true,
		"delete_local_compress": true,
		"upload_to_root": false,
		"deleteRemote": false,
		"distPath": [],
		"remotePath": "/www/wwwtest/test",
		"excludePath": []
	}
	//参考配置
	//环境名称,支持自定义名称
	// "test": { //测试环境
	//     "host": "0.0.0.0", // (必填)服务器地址
	//     "port": 22, // (非必填) 端口号 ,默认22
	//     "username": "username", // (必填)登录用户名
	//     "password": "password", // 登录密码 (和私钥路径,二选一)
	//     // "privateKeyPath": "/your_path/id_rsa", // 私钥路径 (和登录密码,二选一),注意:最好不要将密匙,放代码根目录
	//   "proxy": false, // 是否使用代理,默认false
	//     "upload_on_save": false, // 保存后实时提交,建议单人开发使用,upload_on_save设置为true时,watch、submit_git_before_upload、compress、deleteRemote无效,默认false
	//     "watch": true, // 监听上传目录文件变动,默认true,如果upload_on_save为true,则此项无效。如果配置了distPath目录,则只监听distPath目录下文件变动
	//     "submit_git_before_upload": true, // 团队开发使用,上传代码前提交本地git,防止覆盖远程代码,默认false
	//     "submit_git_msg": "", // 提交git的message配置,默认空。submit_git_before_upload为true时,不填写会弹出提示框手动填写
	//     // "build": "yarn build:test", // (非必填) 构建执行的命令 如果是前端项目则打开此项
	//     "compress": true, //  是否压缩上传,并远程解压(账号需要支持ssh登录,系统会自动检测是否支持,不支持,则不会压缩上传),默认false
	//"remote_unpacked": true, // 压缩上传后是否远程解压,默认true
	//"delete_remote_compress": true, // 压缩文件上传后是否删除远程压缩文件,默认true
	//"delete_local_compress": true, // 压缩文件上传后是否删除本地压缩文件,默认true
	//     "upload_to_root": false, // 如果distPath配置目录只有一个,则上传到remotePath根目录,一般用于部署前端代码, 默认false
	//     "deleteRemote": false, // 上传前是否删除远程distPath配置目录,一般用于清理前端部署代码,  默认false
	//     "distPath": [], // (非必填) 本地需要上传的目录,支持字符串或数组,默认上传根目录
	//     "remotePath": "/www/wwwtest/test", // (必填)上传服务器地址
	//     "excludePath": [] // (非必填) 当前环境排除的上传文件及目录,会和插件配置excludePath合并,插件配置使用gitignore的时候,会和.gitignore配置文件合并
	// }
}

最后再在package.json加上运行语句

js 复制代码
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "deploy": "npm run build && node scripts/node_ssh.js" //->这个是运行语句
  },

🎯 总结:你的部署魔法

现在你拥有了:

  • ✅ 一键连接服务器的能力
  • ✅ 自动定位dist目录的智慧
  • ✅ 批量上传文件的效率
  • ✅ 安全部署的最佳实践 下次部署时,只需轻轻一句:
js 复制代码
  npm run deploy

然后就可以喝着咖啡☕,看着文件自动飞上服务器啦!是不是比手动FTP一个个传文件要优雅多了?快去试试吧!

相关推荐
天天扭码26 分钟前
从数组到对象:JavaScript 遍历语法全解析(ES5 到 ES6 + 超详细指南)
前端·javascript·面试
拉不动的猪28 分钟前
前端开发中常见的数据结构优化问题
前端·javascript·面试
街尾杂货店&28 分钟前
css word
前端·css
Мартин.31 分钟前
[Meachines] [Hard] CrimeStoppers LFI+ZIP-Shell+Firefox-Dec+DLINK+rootme-0.5
前端·firefox
冰镇生鲜32 分钟前
快速静态界面 MDC规则约束 示范
前端
技术与健康1 小时前
【解读】Chrome 浏览器实验性功能全景
前端·chrome
Bald Monkey1 小时前
【Element Plus】解决移动设备使用 el-menu 和 el-sub-menu 时,子菜单需要点击两次才会隐藏的问题
前端·elementui·vue·element plus
小小小小宇1 小时前
PC和WebView白屏检测
前端
天天扭码1 小时前
ES6 Symbol 超详细教程:为什么它是避免对象属性冲突的终极方案?
前端·javascript·面试
小矮马1 小时前
React-组件和props
前端·javascript·react.js