前端Node项目发布流程
最近在做前端的发布流程,发布流程的主要实现以下几个方面:
构建:包括JavaScript、css、html等的压缩,以及版本控制,利用md5生成版本号替换文件引用,实现长缓存策略。
发布:输出新版本的代码,切换系统到新版本
回滚:如果系统有问题,可以切换到原有版本
构建
整个流程由gulp控制,webpack主要处理模块化管理方面的处理,包括基于CommonJs模块规范的包管理,基于SCSS的模块化。
利用Webpack实现JavaScript打包压缩、SCSS编译、CSS文件抽取。
利用gulp-prefix实现cdn url替换
版本替换(revision),利用gulp插件gulp-rev和gulp-rev-all实现文件引用分析和版本替换。
发布
发布时,系统会生成新的版本号,将代码输出到对应版本的目录下,完全与其他版本代码隔离,系统重启后切换到新的版本目录。
回滚
系统可以整体回滚到前一个版本,将系统启动路径切换到上个版本。
实现
目录结构
- source:存放系统源代码
- release:存放系统发布的每个版本的代码,子目录是每个版本的版本号
- logs:存放系统运行日志,pm2的日志存储在这个目录。
- current:软链接,链接到release下面的某个版本的目录
构建流程
完成构建后,代码会发布到release目录下。
gulp流程代码如下:
var gulp = require('gulp');
var minimist = require('minimist');
var uglify = require('gulp-uglify');
var minifyHtml = require('gulp-minify-html');
var minifyCss = require('gulp-minify-css');
var rev = require('gulp-rev');
var revReplace = require('gulp-rev-replace');
var prefix = require('gulp-prefix');
var zip = require('gulp-zip');
var gulpSequence = require('gulp-sequence');
var RevAll = require('gulp-rev-all');
var syncy = require('syncy');
var dateFormat = require('dateformat');
var webpack = require("webpack");
var gutil = require("gulp-util");
var nodemon = require('gulp-nodemon');
var gls = require('gulp-live-server');
var webpackConfig = require("./webpack.config.js");
var path = require('path');
var revHash = require('rev-hash');
/**
* 生产环境构建
*/
var date = dateFormat(new Date(), 'yyyymmddhh');
var version = date;
var options = minimist(process.argv.slice(2), {
string: 'v',
default: { v: date }
});
if (options.v)
version = options.v;
var option = {
src: '.',
dest: '../release/' + version + '/',
cdn: 'http://localhost:8082/',///poster/
static: '../release/' + version + '/public/'
}
//统一加MD5之后替换引用
gulp.task('rev', function () {
var revAll = new RevAll({ dontRenameFile: [/^\/.*.html/] });// ,/^\/.*.jpg|png/
gulp.src([option.src + '/public/**'])
.pipe(revAll.revision())
.pipe(gulp.dest(option.static))
.pipe(revAll.versionFile())
.pipe(gulp.dest(option.static))
.pipe(revAll.manifestFile())
.pipe(gulp.dest(option.static));
});
gulp.task("rep", function () {
var manifest = gulp.src(option.static + "/rev-manifest.json");
return gulp.src([option.src + '/app/views/**/*.ejs'])
.pipe(revReplace({ manifest: manifest, replaceInExtensions: ['.ejs'] }))
.pipe(gulp.dest((option.dest + '/app/views/')))
}); gulp.task('syncfile', (done) => {
syncy([
option.src + '/**',
'!' + option.src + '/.*',
'!' + option.src + '/public/**',
'!' + option.src + '/app/views/**'], option.dest)
.then(() => {
done();
})
.catch((err) => {
done(err);
});
});
gulp.task('cdn_ejs', function () {
console.log('EJS加CDN前缀...');
return gulp.src(option.dest + '/app/views/**/*.ejs')
.pipe(prefix(option.cdn, null))
.pipe(gulp.dest(option.dest + '/app/views/'));
})
gulp.task('cdn_html', function () {
console.log('HTML加CDN前缀...');
return gulp.src([option.static + '/**/*.html'])
.pipe(prefix(option.cdn, null))
.pipe(gulp.dest(option.static));
})
gulp.task('htmlmin', function () {
return gulp.src([option.static + '/**/*.html'])
.pipe(minifyHtml())
.pipe(gulp.dest(option.static));
})
//构建js和css
gulp.task("webpack:build", function (callback) {
// modify some webpack config options
var myConfig = Object.create(webpackConfig);
myConfig.plugins = myConfig.plugins.concat(
new webpack.DefinePlugin({
"process.env": {
// This has effect on the react lib size
"NODE_ENV": JSON.stringify("production")
}
}),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin()
);
// run webpack
webpack(myConfig, function (err, stats) {
if (err) throw new gutil.PluginError("webpack:build", err);
gutil.log("[webpack:build]", stats.toString({
colors: true
}));
callback();
});
});
//构建任务
gulp.task('build', ['webpack:build'], function (cb) {
gulpSequence('rep', ['cdn_ejs', 'cdn_html'], ['htmlmin'], cb)
})
webpack配置如下:
var webpack = require('webpack');
var path = require('path');
var fs = require('fs');
//读取文件夹内的文件列表
var files = fs.readdirSync('./public/module/');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
//以文件名作为属性组装配置文件
var config = {};
files.forEach(function (file) {
var cfgs = require('./public/module/' + file)['entry'];
var entrys = [];
cfgs.forEach(function (cfg) {
entrys.push(path.resolve(__dirname, './public/', cfg));
})
config[path.parse(file).name] = entrys;
})
console.log(config);
module.exports = {
entry: config,
output: {
path: path.join(__dirname, "./public/build/"),
filename: "[name]/[name].entry.js",
chunkFilename: "[id].js",
publicPath: "assets/"
},
module: {
loaders: [
// Extract css files
{
test: /\.css$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
},
// Optionally extract less files
// or any other compile-to-css language
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader!sass-loader")
}
// You could also use other loaders the same way. I. e. the autoprefixer-loader
]
},
// Use the plugin to specify the resulting filename (and add needed behavior to the compiler)
plugins: [
new ExtractTextPlugin("[name]/[name].css")
]
};
系统切换
执行bash脚本,首先删除current软链接,删除软链接并不影响当前系统,因为当前系统链接到的是Release目录的某个版本的目录,删除后重新建立软链接,链接到新的版本目录下,然后利用pm2重启。
echo 【delete current link】
rm -rf current
echo 【create current link】
ln -s release/$version current echo 【restarting application......】
cd current
pm2 startOrRestart ./ecosystem.json --env production
在代码路径下,添加了一个build.sh的脚步,每个构建执行,完整脚步如下:
version=$(date +%Y%m%d%H%m)
echo 【start build $version】
echo 【updating project .....】
git pull
echo 【finish update!】
echo 【updating environment......】
npm install
echo 【finish update!】
echo 【building......】
gulp syncfile -v $version
gulp rev -v $version
gulp build -v $version
echo 【finish build】
cd ../
echo 【delete current link】
rm -rf current
echo 【create current link】
ln -s release/$version current echo 【restarting application......】
cd current
pm2 startOrRestart ./ecosystem.json --env production
脚本使用日期作为新的版本号。
前端Node项目发布流程的更多相关文章
- Linux项目发布流程
Linux项目发布流程(一) 1.安装pyhton3.7 的依赖包 yum -y groupinstall "Development tools" yum -y install z ...
- node项目发布+域名及其二级域名配置+nginx反向代理+pm2
学习node的时候也写了一些demo.但是只是限于本地测试,从来没有发布.今天尝试发布项目. 需要准备的东西 node 项目:为了突出重点,说明主要问题.我只是拿express 写了很简单的demo. ...
- node项目的基本构建流程或者打开一个node项目的流程
1. 确立项目所需要的所有依赖.框架(比如bootstrap,vue,angular等) 2. 在项目的根目录下创建一个package.json文件,package.json文件是项目的最重要文件之 ...
- Jenkins构建 前端node项目
1.新建一个自由风格的项目 2.配置git 3.构建-增加构建步骤-执行shell cd $WORKSPACE npm install --registry=http://ip:port --unsa ...
- linux单项目发布流程
1.安装python #1.安装python3.7所需要的依赖包yum -y groupinstall "Development tools"yum -y install zlib ...
- linux 下安装 python ngix 项目发布流程
1.安装python #1.安装python3.7所需要的依赖包yum -y groupinstall "Development tools"yum -y install zlib ...
- docker jenkins 前端node项目 自动化部署异常 env: ‘node’: No such file or directory
出现问题是docker jenkins 里面没有自动安装node导致找不到这个Node命令 解决方案:手动安装nodejs # 进入jenkins对应容器中 # docker exec -it [对应 ...
- k8s:py项目发布完整流程
k8s:py项目发布流程 1. 编写Dockerfile # cat Dockerfile FROM python:3.6-slim USER root RUN apt-get update & ...
- 基于bat脚本的前端发布流程的优化
背景介绍 前面在基于bat脚本的前端发布流程设计与实现中,我已经介绍了设计与实现,这一篇主要是针对其的一个优化折腾(分两步走,第一步先搞出来,第二步再想着怎么去优化它),我主要做了以下几件事. &qu ...
随机推荐
- AQS之Condition
一.引言 一般我们在使用锁的Condition时,我们一般都是这么使用,以ReentrantLock为例, ReentrantLock lock = new ReentrantLock(); Cond ...
- 利用bing图片搜索接口开发图片搜索应用程序
概述:通过bing的图片搜索引擎,开发自己的图片搜索应用程序.bing的图片搜索接口是收费的,但是初次注册使用,key可以免费试用30天 程序运行效果如下 一,代码如下 static SearchRe ...
- drop与truncate与delete的区别与联系
在mysql和oracle数据库中delete与truncate都是可以用来对数据进行删除操作,但是二者又有些不同. 主要有以下几个区别: 区别一: 根据sql语言分类来说,delete属于DML语言 ...
- jQuery改变元素class属性
//去掉class属性 $(this).parent('li').removeClass("prev_selected"); //去掉同兄弟的class属性. $(this).pa ...
- Big Data(四)关于Hadoop的HA&CAP理论详解
问题 思路: 主从集群:结构相对简单,主与从协作 主:单点,数据一致好掌握 问题: 单点故障,集群整体不可用 压力过大,内存受限 解决方案 单点故障: 高可用方案:HA(High Available) ...
- Codeforces1204C. Anna, Svyatoslav and Maps (贪心 + Floyd)
题目链接:传送门 题目大意: 给出n<=100的有向图,和路径p,求p的最短子序列v,使得依次经过v中所有点的路径为p. 思路: 题意其实就是让我们求路径上的一些关键点v,对于所有的关键点:vi ...
- Atcoder Regular 098 区间Pre=Xor Q询问区间连续K去最小值最小极差
C 用scanf("%s")就会WA..不知道为什么 /*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset ...
- Lindström–Gessel–Viennot lemma
解决不相交路径计数 有两个大小为N的点集A,B A上每一个点对应着B的每一个点 求满足条件的路径集合有多少个 图里面可能还有一些障碍 Codeforces 348 D 有一个N*M的网格图 有两个点 ...
- TJOI2017DNA
P3763 [TJOI2017]DNA 字符串匹配,字符集大小为\(4\),认为相差不超过\(3\)即合法. 对每一种字符分开考虑不同产生的贡献. 对于串\(S\),如果当前位置相同则\(S_i=1\ ...
- SpringMVC初识
1 SpringMVC的概述 Spring为展现层提供的基于MVC设计理念的优秀的web框架,是目前最主流的MVC框架之一. Spring3.0后面全面超过Struts2,成为了最优秀的MVC框架. ...