嘿,前端界的小伙伴们,又到了把代码部署到服务器的那一天了!我知道你们的内心:有些人听到“CICD、Jenkins”就头疼,有些人还得把打包好的文件递给负责人,搞得大家都不太愉快。想要一个既简单又能一键部署的方法吗?来,用我们最熟悉的JavaScript语言,Node来搞定它!
首先,Node就是这么全能,有句话怎么说的来着?如果可以用JS做,最终都会用JS来做!我们用一些Node的神奇依赖库,只需要一个命令,就能自动完成部署,是不是很酷?
- child_process:这货能让你用JS执行npm命令,打包你的项目。
- archiver:帮你把文件压成tar包,方便传输。
- ssh2:连接到远程服务器,执行Linux命令,把你的tar包传过去并解压到Nginx的指定位置。
接下来的部分,我们以Vue为例(React的小伙伴微调一下也能用),详细的部署脚本可以在这个代码仓库找到。
代码仓库在 (前端部署简易脚本deploy.js · Duck/EmpiricalCase - 码云 - 开源中国 (gitee.com)),可以下载下来看完整代码
一、环境变量
先搞定一些变量,方便我们后面的操作。
const cp = require('child_process')
const ssh = require('ssh2')
const archiver = require('archiver')
const fs = require('fs')
const path = require('path')
// 命令执行的模式
const mode = process.argv.slice(2)[0] || 'test'
// 打包后的名称
const distName = 'dist.tar.gz'
// 不同环境打包变量
const unzipDirMode = {
test: {
spawn: 'pnpm build:test',
// 解压文件名
unzipDir: 'back_end/',
// 终端定位位置 cd
servicePath: '/workspace/test',
// 压缩包存放位置
serviceFilePath: `/workspace/test/${distName}`
},
production: {
spawn: 'pnpm build:prod',
unzipDir: 'back_end/',
servicePath: '/workspace/pro/',
serviceFilePath: `/workspace/pro/${distName}`
}
}
// 文件所在地
const distPath = path.resolve(__dirname, '../dist')
// 打包后位置
const zipPath = path.resolve(__dirname, `../${distName}`)
// 远程服务器存放位置
const { spawn, servicePath, serviceFilePath, unzipDir } = unzipDirMode[mode]
// 服务器连接信息
const connectInfo = {
host: 'ip',
port: '端口',
username: '服务器用户名',
password: '服务器密码'
}
二、打包
运行npm run build,用pnpm的话代码会更短些。
start()
// 入口函数
async function start() {
try {
// 1. 打包
await build()
} catch (error) {
console.error('Error:', error.message)
} finally {
}
}
/**
* 1. 本地构建项目
*/
function build() {
return new Promise((resolve, reject) => {
//对项目进行打包,然后生成压缩文件
let pro = cp.spawn(spawn, {
shell: true,
stdio: 'inherit'
})
pro.on('exit', code => {
if (code === 0) {
console.log('---构建成功---')
resolve()
} else {
reject(new Error('构建失败'))
}
})
})
}
三、压缩
我们用tar格式压缩,这样服务器上解压就不需要额外依赖了。
/**
* 2. 将打包后文件压缩tar
* @returns
*/
function startZip() {
return new Promise((resolve, reject) => {
console.log('---开始打包tar---')
//定义打包格式和相关配置
const archive = archiver('tar', {
gzip: true, // 如果需要压缩,可以使用 gzip
gzipOptions: { level: 9 } // gzip 压缩级别
}).on('error', err => reject(err))
const output = fs.createWriteStream(zipPath)
//监听流的打包
output.on('close', () => {
console.log('---目标打包完成---')
resolve(true)
})
//开始压缩
archive.pipe(output)
// 文件夹压缩
archive.directory(distPath, false)
archive.finalize()
})
}
四、文件传输
连接服务器,把tar包传上去。
//链接服务器
let conn = new ssh.Client()
/**
* 3. 将zip文件传输至远程服务器
*/
function connect() {
return new Promise((resolve, reject) => {
conn
.on('ready', () => {
conn.sftp((err, sftp) => {
if (err) {
return reject(err)
}
sftp.fastPut(zipPath, serviceFilePath, {}, (err, result) => {
if (err) {
return reject(err)
}
//开始上传
console.log('---压缩包上传成功---')
resolve()
})
})
})
.on('error', err => reject(err))
.connect(connectInfo)
})
}
五、远程命令
在服务器上解压部署。
/**
* 4. 解压部署操作
* @param {*} conn
*/
async function shellCmd(conn) {
return new Promise((resolve, reject) => {
conn.shell((err, stream) => {
if (err) {
return reject(err)
}
//进入服务器暂存地址
//解压上传的压缩包
//移动解压后的文件到发布目录
//删除压缩包
//退出
const commands = `
cd ${servicePath} &&
mkdir -p ${unzipDir} &&
tar -xzf ${distName} -C ${unzipDir} &&
rm -rf ${distName} &&
exit
`
console.log('--- 终端执行命令:', commands)
stream
.on('close', () => resolve())
.on('data', data => console.log(data.toString()))
.stderr.on('data', data => console.error('Error:', data.toString()))
stream.end(commands)
})
})
}
六、清理现场
部署完成后,别忘了删除本地的tar包。
/**
* 5. 删除本地的dist.zip
*/
function delZip() {
fs.unlink(zipPath, function (err) {
if (err) {
console.error('删除文件失败:', err.message)
}
console.log('---文件:' + zipPath + '删除成功!---')
})
}
结束
最后,把所有步骤包在一个start函数里,跑一下,整个部署过程就顺滑完成了。加一条npm命令,下次直接跑这个命令,你的代码就能飞上云端。是不是听起来就很有趣?赶快试试吧!
async function start() {
try {
// 1. 打包
await build()
// 2. 压缩zip
await startZip()
// 3. 将zip文件传输至远程服务器
await connect()
// 4. 部署解压
await shellCmd(conn)
console.log('---部署完成---')
} catch (error) {
console.error('Error:', error.message)
} finally {
// 5. 断开ssh,并删除本地压缩包
conn.end()
delZip()
}
}
"scripts": {
"dev": "vite --mode development",
"build:test": "vite build --mode test",
"build:prod": "vite build --mode production",
"deploy:test": "node ./script/deploy test",
"deploy:prod": "node ./script/deploy production",
}
pnpm deploy:test pnpm deploy:prod