🤯 什么?!还在跪求后端大佬帮你上线项目?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一个个传文件要优雅多了?快去试试吧!

相关推荐
辻戋2 小时前
从零实现React Scheduler调度器
前端·react.js·前端框架
徐同保2 小时前
使用yarn@4.6.0装包,项目是react+vite搭建的,项目无法启动,报错:
前端·react.js·前端框架
Qrun3 小时前
Windows11安装nvm管理node多版本
前端·vscode·react.js·ajax·npm·html5
中国lanwp3 小时前
全局 npm config 与多环境配置
前端·npm·node.js
JELEE.4 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl6 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫7 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友7 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理9 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻9 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js