期望:
思路
实现 (代码比较潦草,需要整理)
deploy.js
// /deploy.js
const scpClient = require("scp2");
const exec = require("child_process").exec;
const execSync = require("child_process").execSync;
const readline = require("readline");
/*
*定义多个服务器账号
*/
const SERVER_LIST = {
// 测试环境
test: {
name: "测试环境",
host: "", // ip
port: 22, // 端口
username: "root", // 登录服务器的账号
password: "@", // 登录服务器的账号
path: "/home/static/cnhpec", // 发布至静态服务器的项目路径
outputDir: "dist/" // 打包的目录 对应vue.config的outputDir配置
},
// 开发环境
prod: {
name: "生产环境",
host: "",
port: 22,
username: "root",
password: "@",
path: "/home/static/cnhpec",
outputDir: "dist/"
}
};
const argv = process.argv.slice(2);
const env = argv[0] ? (argv[0] === "prod" ? "prod" : "test") : "test";
const indexFile = SERVER_LIST[env].path + "/index.html";
const Client = require("ssh2").Client;
const conn = new Client();
conn
.on("ready", function() {
init();
})
.on("error", function(err) {
if (err) throw err;
})
.connect({
host: SERVER_LIST[env].host,
port: SERVER_LIST[env].port,
username: SERVER_LIST[env].username,
password: SERVER_LIST[env].password
});
async function init() {
await pull(); // 拉git最新代码
await build(); // 打包
await rename(); // 备份服务器代码,生成版本
await upload(); // 上传代码
}
function pull() {
return new Promise(function(resolve, reject) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
select();
function select() {
rl.question(
"此操作git项目强制与远端同步!!!本地代码没提交不要轻易进行操作!!!\n\n此操作git项目强制与远端同步!!!本地代码没提交不要轻易进行操作!!!\n\n此操作git项目强制与远端同步!!!本地代码没提交不要轻易进行操作!!!(y/n)",
function(answer) {
answer = answer.trim().toLowerCase();
if (answer === "y") {
const branch = execSync("git symbolic-ref --short HEAD");
exec(`git fetch --all && git reset --hard origin/${branch.toString()} && git pull`, function(err, stdout, stderr) {
if (err) {
console.error("项目强制与远端同步失败!!!");
reject();
throw err;
}
console.info(stdout.toString());
console.error(stderr);
console.log("项目强制与远端同步成功");
resolve();
});
rl.close();
} else if (answer === "n") {
reject();
rl.close();
process.exit(0);
} else {
select();
}
}
);
}
});
}
function build() {
return new Promise(function(resolve, reject) {
console.log(`开始打包${env === "prod" ? "生产" : "测试"}环境。。。。。。`);
exec("npm run " + env, function(err, stdout, stderr) {
if (err) {
console.error("打包失败!!!");
reject();
process.exit(1);
throw err;
}
console.info(stdout.toString());
console.error(stderr);
console.log("打包成功");
resolve();
});
});
}
function rename() {
return new Promise(function(resolve, reject) {
console.log("开始备份服务器版本。。。。。。");
conn.exec("stat " + indexFile, function(err, stream) {
if (err) {
console.error("服务器版本备份失败。。。。。。");
conn.end();
reject();
process.exit(1);
throw err;
}
let mtime;
stream
.on("close", function(code) {
if (code == 0) {
copy(mtime, resolve, reject);
} else {
resolve();
}
})
.on("data", function(data) {
console.info(data.toString());
mtime = data.toString().split("\n")[7];
mtime = mtime.replace(/[\u4e00-\u9fa5]/g, "").replace(":", "");
mtime = formatDate(mtime);
})
.stderr.on("data", function(data) {
console.warn(data.toString());
});
});
});
}
function copy(mtime, resolve, reject) {
conn.exec("/bin/cp " + indexFile + " " + SERVER_LIST[env].path + "/index." + mtime + ".html", function(err, stream) {
if (err) {
console.error("服务器版本备份失败。。。。。。");
conn.end();
reject();
process.exit(1);
throw err;
}
stream
.on("close", function(code) {
if (code == 0) {
console.log("服务器版本备份成功");
resolve();
} else {
console.error("服务器版本备份失败。。。。。。");
conn.end();
reject();
process.exit(1);
}
})
.on("data", function(data) {
console.info(data.toString());
})
.stderr.on("data", function(data) {
console.error(data.toString());
});
});
}
function upload() {
return new Promise(function(resolve, reject) {
scpClient.scp(
SERVER_LIST[env].outputDir,
{
host: SERVER_LIST[env].host,
port: SERVER_LIST[env].port,
username: SERVER_LIST[env].username,
password: SERVER_LIST[env].password,
path: SERVER_LIST[env].path
},
function(err) {
conn.end();
if (err) {
reject();
process.exit(1);
throw err;
} else {
console.log("Success! 成功发布到" + (env === "prod" ? "生产" : "测试") + "服务器!");
resolve();
process.exit(0);
}
}
);
});
}
function formatDate(date) {
const isZero = m => (m < 10 ? "0" + m : m),
time = new Date(date),
y = time.getFullYear(),
m = time.getMonth() + 1,
d = time.getDate(),
h = time.getHours(),
mm = time.getMinutes(),
s = time.getSeconds();
return y + "" + isZero(m) + "" + isZero(d) + "" + isZero(h) + "" + isZero(mm) + "" + isZero(s);
}
package.json添加部署命令
{
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build --mode build",
"test": "vue-cli-service build --mode test",
"lint": "vue-cli-service lint",
"i18n:report": "vue-cli-service i18n:report --src './src/**/*.?(js|vue)' --locales './src/locales/**/*.json'",
"deploy:test": "node ./deploy test",
"deploy:prod": "node ./deploy prod"
},
"devDependencies": {
"scp2": "^0.5.0"
}
}
总结
前端部署其实也算前端工程化的一部分,这篇博客是我结合实际工作经验和业余探索,总结出的前端部署的一些尝试。一方面自己有所记录,另一方面也能给大家带来些启示。
相信很多前端同学对 vue 或 react 的开发很熟悉了,也知道如何去打包生成一个生产环境的包,但对于生产环境的部署可能有些同学了解比较少。小公司可能都是后端帮忙部署了,大公司会有专门的运维同学部署
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!