前言:
一般来说,我们前端是不需要关心部署的事情的,只需要把打包后的文件直接丢给后台去部署就可以了。但是呢,如果频繁修改一点东西就要叫后台进行部署,这样后台会很烦(毕竟人家还有其他工作嘛),我们也会很不好意思。
或许有些公司会给前端配置可视化操作服务器文件的软件(FTP客户端),这时我们就可以打包后自己到服务器上部署了,如果不同环境需要部署到不同服务器,此时我们又需要区分打包再手动上传到服务器上。
这时我们就会想,有没有直接一句命令就能自动化部署到不同服务器上,根本不需要打开软件来手动上传的???
答案:必须有啊,接下来看看如何进行操作,一劳永逸~~

一、webpack + Nodejs实现前端自动部署 ——完整版(法一)

这种方式就是完全由我们前端工程师来实现的啦,通过写nodejs实现服务器操作,结合webpack打包完成自动部署。

1、首先我们用nodejs来封装一个能操作远程服务器的工具库

文件命名为:serverLib.js

/*** 该文件封装了对远程服务器的操作*/const util = require('util');const events = require('events');const { Client } = require('ssh2'); // ssh2模块需要使用npm安装const fs = require('fs');const path = require('path');/*** 描述:连接远程电脑* 参数:server 远程电脑凭证;then 回调函数* 回调:then(conn) 连接远程的client对象*/function Connect(server, then) {const conn = new Client();conn.on('ready', () => {then(conn);}).on('error', (err) => {// console.log("connect error!");}).on('end', () => {// console.log("connect end!");}).on('close', (had_error) => {// console.log("connect close");}).connect(server);}/*** 描述:运行shell命令* 参数:server 远程电脑凭证;cmd 执行的命令;then 回调函数* 回调:then(err, data) : data 运行命令之后的返回数据信息*/function Shell(server, cmd, then) {Connect(server, (conn) => {conn.shell((err, stream) => {if (err) {then(err);} else { // end of iflet buf = '';stream.on('close', () => {conn.end();then(err, buf);}).on('data', (data) => {buf += data;}).stderr.on('data', (data) => {console.log(`stderr: ${data}`);});stream.end(cmd);}});});}/*** 描述:上传文件* 参数:server 远程电脑凭证;localPath 本地路径;remotePath 远程路径;then 回调函数* 回调:then(err, result)*/function UploadFile(server, localPath, remotePath, then) {Connect(server, (conn) => {conn.sftp((err, sftp) => {if (err) {then(err);} else {sftp.fastPut(localPath, remotePath, (err, result) => {conn.end();then(err, result);});}});});}/*** 描述:下载文件* 参数:server 远程电脑凭证;remotePath 远程路径;localPath 本地路径;then 回调函数* 回调:then(err, result)*/function DownloadFile(server, remotePath, localPath, then) {Connect(server, (conn) => {conn.sftp((err, sftp) => {if (err) {then(err);} else {sftp.fastGet(remotePath, localPath, (err, result) => {if (err) {then(err);} else {conn.end();then(err, result);}});}});});}/*** 描述:获取远程文件路径下文件列表信息* 参数:server 远程电脑凭证;*       remotePath 远程路径;*       isFile 是否是获取文件,true获取文件信息,false获取目录信息;*       then 回调函数* 回调:then(err, dirs) : dir, 获取的列表信息*/function GetFileOrDirList(server, remotePath, isFile, then) {const cmd = `find ${remotePath} -type ${isFile == true ? 'f' : 'd'}\r\nexit\r\n`;Shell(server, cmd, (err, data) => {let arr = [];const remoteFile = [];arr = data.split('\r\n');arr.forEach((dir) => {if (dir.indexOf(remotePath) == 0) {remoteFile.push(dir);}});then(err, remoteFile);});}/*** 描述:控制上传或者下载一个一个的执行*/function Control() {events.EventEmitter.call(this);}util.inherits(Control, events.EventEmitter); // 使这个类继承EventEmitterconst control = new Control();control.on('donext', (todos, then) => {if (todos.length > 0) {const func = todos.shift();func((err, result) => {if (err) {throw err;then(err);} else {control.emit('donext', todos, then);}});} else {then(null);}});/*** 描述:下载目录到本地* 参数:server 远程电脑凭证;*       remotePath 远程路径;*       localDir 本地路径,*       then 回调函数* 回调:then(err)*/function DownloadDir(server, remoteDir, localDir, then) {GetFileOrDirList(server, remoteDir, false, (err, dirs) => {if (err) {throw err;} else {GetFileOrDirList(server, remoteDir, true, (err, files) => {if (err) {throw err;} else {dirs.shift();dirs.forEach((dir) => {const tmpDir = path.join(localDir, dir.slice(remoteDir.length + 1)).replace(/[//]\g/, '\\');// 创建目录fs.mkdirSync(tmpDir);});const todoFiles = [];files.forEach((file) => {const tmpPath = path.join(localDir, file.slice(remoteDir.length + 1)).replace(/[//]\g/, '\\');todoFiles.push((done) => {DownloadFile(server, file, tmpPath, done);console.log(`downloading the ${file}`);});// end of todoFiles.push});control.emit('donext', todoFiles, then);}});}});}/*** 描述:获取windows上的文件目录以及文件列表信息* 参数:destDir 本地路径,*       dirs 目录列表*       files 文件列表*/function GetFileAndDirList(localDir, dirs, files) {const dir = fs.readdirSync(localDir);for (let i = 0; i < dir.length; i++) {const p = path.join(localDir, dir[i]);const stat = fs.statSync(p);if (stat.isDirectory()) {dirs.push(p);GetFileAndDirList(p, dirs, files);} else {files.push(p);}}}/*** 描述:上传文件夹到远程目录* 参数:server 远程电脑凭证;*       localDir 本地路径,*       remoteDir 远程路径;*       then 回调函数* 回调:then(err)*/function UploadDir(server, localDir, remoteDir, then) {const dirs = [];const files = [];GetFileAndDirList(localDir, dirs, files);// 删除远程指定目录下的所有文件const deleteDir = [(done) => {const cmd = `rm -rf ${remoteDir}* \r\nexit\r\n`;console.log(cmd);Shell(server, cmd, done);}];// 创建远程目录const todoDir = [];dirs.forEach((dir) => {todoDir.push((done) => {const to = path.join(remoteDir, dir.slice(localDir.length)).replace(/[\\]/g, '/');const cmd = `mkdir -p ${to}\r\nexit\r\n`;console.log(cmd);Shell(server, cmd, done);});// end of push});// 上传文件const todoFile = [];files.forEach((file) => {todoFile.push((done) => {const to = path.join(remoteDir, file.slice(localDir.length)).replace(/[\\]/g, '/');console.log(`upload ${to}`);UploadFile(server, file, to, done);});});control.emit('donext', deleteDir, (err) => {if (err) {throw err;} else {control.emit('donext', todoDir, (err) => {if (err) {throw err;} else {control.emit('donext', todoFile, then);}});}});}exports.Shell = Shell;exports.UploadFile = UploadFile;exports.DownloadFile = DownloadFile;exports.GetFileOrDirList = GetFileOrDirList;exports.DownloadDir = DownloadDir;exports.UploadDir = UploadDir;
2、封装一个webpack插件

该插件实现webpack打包后将打包目录文件上传到服务器上。
文件命名为:uploadFileWebPackPlugin.js

/*** 上传打包后的文件到服务器上的webpack插件*/const { spawn } = require('child_process');const uploadDir = require('./serverLib').UploadDir;class UploadFileWebPackPlugin {constructor(options) {this.options = options;}apply(compiler) {// 定义在打包后执行这个webpack插件// 需要用到对应的钩子函数compiler.hooks.done.tap('upload-file-plugin', async (status) => {// console.log('this.options: ', this.options);this.deploy();});}deploy() {const chmod = spawn('chmod', ['-R', '777', this.options.buildFolder]);chmod.on('exit', (code, signal) => {console.log('\n服务器授权成功,开始自动化部署~~\n');uploadDir(this.options.serverConfig,this.options.buildFolder,this.options.servePath,(err) => {if (err) throw err;console.log('\n自动化部署成功~\n');},);});}}module.exports = UploadFileWebPackPlugin;

至于webpack插件如何编写,语法是什么?下面推荐几篇文章大家参考下。
怎样编写一个简单的webpack插件
Webpack原理-编写Plugin
webpack官网-编写自定义插件

3、在webpack配置文件的plugins配置项中引入上面自定义的插件

这里我们以vue-cli脚手架来举例,其他项目的引入方式雷同。
这里需要根据我们设定的运行命令参数,和远程服务器的信息进行对应修改即可。
对上面的截图进行代码更正,可根据实际测试结果进行修改。

// vue.config.js文件
/*** 自动化部署代码引入  start*/
// 引入自定义的上传文件webpack插件
const UploadFileWebPackPlugin = require('./webpack-plugin/uploadFileWebPackPlugin');// 获取运行命令的参数
const deployArgv = process.argv.pop();
// 通过参数判断是否要执行上传插件
let isNeedUpload = false;
let uploadServerConfig = {};
// 根据参数设置不同服务器信息
if (deployArgv === '-95') {isNeedUpload = true;uploadServerConfig = {host: 'xxx.xxx.xxx.95', // 服务器ip地址port: 55314, // 服务器端口号username: 'xxxxx', // 登录服务器的用户名password: 'xxxxxxx', // 登录服务器的密码};
} else if (deployArgv === '-114') {isNeedUpload = true;uploadServerConfig = {host: 'xxx.xxx.xxx.114',port: 55314,username: 'xxxxx',password: 'xxxxxxxxx',};
}
/*** 自动化部署代码引入  end*/
const webpackConfig = {configureWebpack: {// plugin配置项plugins: [// // 在npm run build的时候才执行这个插件(自动化部署插件)// ---- 尝试过这个方法使用插件,但是在不加参数的时候就会报错说webpack插件定义不正确的情况// (process.env.NODE_ENV === 'production' && isNeedUpload)//   && new UploadFileWebPackPlugin({//       // 服务器的配置信息//       serverConfig: uploadServerConfig,//       // 本地打包输出的文件夹路径//       buildFolder: 'dist/',//       // 上传到服务器上的路径//       servePath: '/home/sendi/fe/winne-test/',//   }),],},// 暂时关闭eslint校验, 方便测试devServer: {overlay: {warining: true,errors: true,},},lintOnSave: false,// 配置部署应用包时的基本 URLpublicPath: process.env.NODE_ENV === 'production'? '/winne-test/': '/',
};// webpack插件根据环境判断来使用改为这个方式(在加参数或者不加参数的情况都能正确运行)
if ((process.env.NODE_ENV === 'production' && isNeedUpload)) {webpackConfig.configureWebpack.plugins.push(new UploadFileWebPackPlugin({// 服务器的配置信息serverConfig: uploadServerConfig,// 本地打包输出的文件夹路径buildFolder: 'dist/',// 上传到服务器上的路径servePath: '/home/sendi/fe/winne-test/',}),);
}module.exports = webpackConfig;
4、运行打包命令,实现前端项目的自动化部署

1)、没用到自动化部署时,我们这样打包项目
使用npm打包:npm run build
使用yarn打包:yarn build

2)、需要自动化部署时,我们这样打包项目(打包命令后面加参数,识别不同参数部署到不同服务器)
使用npm打包:npm run build -- -95 或者 npm run build -- -114 (注意在参数前有两个中划线)
使用yarn打包:yarn build -95 或者 yarn build -114

最后

如果要更方便的使用,可以把自动部署功能代码直接封装成npm包发布到npm上,这样以后用到时可以直接使用npm下载,就可以使用啦。

二、jenkins实现前端自动部署(法二)

这个方法一般来说都是后端来配置的,此处不展开,感兴趣的伙伴们,自行百度解决,下面推荐几篇文章。

使用jenkins进行前端项目自动部署
一套基础自动化部署搭建过程(vue实战防坑版)
学会使用 Jenkins 自动部署你的项目(实战)

实现前端项目自动化部署(webpack+nodejs)相关推荐

  1. 前端项目自动化部署——超详细教程(Jenkins、Github Actions)

    本教程主要讲解了怎么使用 Jenkins 和 Github Actions 部署前端项目. 第一部分是使用 Gitea 配置局域网 git 服务器,再使用 Jenkins 将 Gitea 下的项目部署 ...

  2. 前端教程——前端项目自动化部署(Jenkins、Github Actions)

    本教程主要讲解了怎么使用 Jenkins 和 Github Actions 部署前端项目. 第一部分是使用 Gitea 配置局域网 git 服务器,再使用 Jenkins 将 Gitea 下的项目部署 ...

  3. 前端项目自动化部署——超详细教程(Jenkins、Github Actions)(转发)

    本教程主要讲解了怎么使用 Jenkins 和 Github Actions 部署前端项目. 第一部分是使用 Gitea 配置局域网 git 服务器,再使用 Jenkins 将 Gitea 下的项目部署 ...

  4. Jenkins+Github+Nginx实现前端项目自动部署

    Jenkins+Github+Nginx实现前端项目自动部署 前言 最近在搭建一个自己的网站,网站框架搭好了要把项目放到服务器运行,但是每次更新网站内容就要手动部署一次,实在很麻烦,于是就想搭建一套自 ...

  5. 运维(1) Jenkinsfile+Dockerfile+Nginx实现前端Vue自动化部署

    一.前言 本文将基于以下环境通过Dockerfile+Jenkinsfile+Nginx 以jenkins流水线的方式实现前端Vue自动化部署 Docker version 1.13.1, build ...

  6. Jenkins + 云效 前后端项目自动化部署

    环境安装 创建目录 后续下载的内容 放在里边方便管理 cd /mkdir dockercd dockermkdir maven mkdir jenkinsmkdir javamkdir registr ...

  7. 前端项目搭建部署全流程(一):搭建React项目

    1.前言 前段时间突发一个想法,想尝试从零开始搭建一个React项目模板,发布到GitHub,再编写脚手架命令拉取模板以及编写脚本命令快速生成业务模块,然后再用这个模板结合之前的一套组件库,完成编译打 ...

  8. 2022年了你必须要学会搭建微前端项目及部署方式

    一.微前端简介 微前端是一种类似于微服务的架构,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用.各个前端应用可以独立运行.独立开发.独立部署. ...

  9. 项目自动化部署与手动部署

    自动化部署 自动化部署参考自 coderwhy. DevOps 传统的开发模式: 项目完成 => build => test => release => operate Dev ...

最新文章

  1. SpringBoot框架(4)-- 类装配及Bean装配监听器
  2. MVVM架构~knockoutjs实现简单的购物车
  3. block关键字的使用
  4. python获取android手机信息
  5. Nhibernate 基础关系映射
  6. 测地膨胀和膨胀重建—lhMorpRDilate
  7. 插入排序算法(C实现)
  8. 读取 Excel 之 Epplus
  9. Arduino与SG90舵机握手
  10. Java的jdk版本梳理
  11. 服装尺寸 html,国家标准服装尺寸表
  12. 算法之迪杰斯特拉算法
  13. 神秘的java Https
  14. 【Android自动化测试】Robot Framework+adb框架(四)——L2层关键字
  15. 【backtrader源码解析52】indicators部分代码解读(枯燥,仅供参考,源代码解析结束,后面会增加一个backtrader框架分析)
  16. 【数据架构系列-02】从《数据中台能力成熟度模型》的发布,聊聊火了的中台
  17. 【文本挖掘】——词频统计、词云绘制与美化+[微微一笑很倾城]实战
  18. ASP.NET中使用动态令牌进行安全认证
  19. lisp封装为vlx方法_将VLDCL的FAS编译进VLX
  20. 相机光学(二十三)——远心镜头

热门文章

  1. QCustomplot 坐标轴和网格线
  2. python2升级为python3
  3. 中国美甲灯行业研究及十四五规划分析报告
  4. 微信小程序 - 空数据、网络加载失败 组件
  5. Java开发框架选型对比:ruoyi与yudao框架
  6. 篱笆家装宝典之三——油漆涂料
  7. Duplicate class com.amap.api.fence.DistrictItem found in modules location-6.2.0 and navi-3dmap-9.6.
  8. XtraBackup 使用总结
  9. ArcGisPro脚本工具【0】——制作你的第一个脚本工具
  10. PHP笔记:move_uploaded_file中文乱码