前端Node项目发布流程

Posted coder_zyz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端Node项目发布流程相关的知识,希望对你有一定的参考价值。

最近在做前端的发布流程,发布流程的主要实现以下几个方面:

构建:包括javascript、css、html等的压缩,以及版本控制,利用md5生成版本号替换文件引用,实现长缓存策略。

发布:输出新版本的代码,切换系统到新版本

回滚:如果系统有问题,可以切换到原有版本

构建

整个流程由gulp控制,webpack主要处理模块化管理方面的处理,包括基于CommonJs模块规范的包管理,基于SCSS的模块化。

利用Webpack实现JavaScript打包压缩、SCSS编译、CSS文件抽取。

利用gulp-prefix实现cdn url替换

版本替换(revision),利用gulp插件gulp-rev和gulp-rev-all实现文件引用分析和版本替换。

发布

发布时,系统会生成新的版本号,将代码输出到对应版本的目录下,完全与其他版本代码隔离,系统重启后切换到新的版本目录。

回滚

系统可以整体回滚到前一个版本,将系统启动路径切换到上个版本。

实现

目录结构

  1. source:存放系统源代码
  2. release:存放系统发布的每个版本的代码,子目录是每个版本的版本号
  3. logs:存放系统运行日志,pm2的日志存储在这个目录。
  4. 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项目发布流程的主要内容,如果未能解决你的问题,请参考以下文章

Node.js JavaScript 片段中的跳过代码

前端Vue+后端Django项目创建以及自动部署流程

前端Vue+后端Django项目创建以及自动部署流程

node 和 reactrewired 修改 React 打包目录的流程

VSCode自定义代码片段——git命令操作一个完整流程

VSCode自定义代码片段15——git命令操作一个完整流程