前端开发自动构建

为了成为一名稍微专业一些的前端开发,我觉得自己了解并使用工具来自动构建前端代码还是很有必要的。工具很多,虽然grunt是一个大伙都说还不错的工具,插件也比较丰富,但gulp是号称要替代grunt的工具,有不少的优点,例如上手简单,代码短,本身就是配置文件,流式处理效率高,看到得错误的行号提示等等,对于两者都没有接触过的人来说,还是用gulp吧。于是元旦在家里没事就折腾了一下。以下是我的使用过程。

目标

前端的工作流程一些是这样: 写代码-》代码检查-》测试-》文件合并、压缩-》发布

前面的代码检查和测试我还没有做,不过目前我需要工具可以帮我做的事有下面两个:

  1. 能够帮我合并压缩代码
  2. 为了避免浏览器缓存问题,对于新发布的代码,需要能加上md5值,并且能自动修改我的html中对js/css的引用

安装

  1. 先从官网安装node.js,里面会带有npm工具。
  2. 再用npm来安装gulpnpm install --global gulp
  3. 进入项目目录,再安装一下项目的依赖npm install --save-dev gulp, --save-dev会自动加入到package.json中
  4. 每一个项目都需要一个gulpfile.js文件,我们后续的代码和配置就都在这个文件里面了,下面是一个例子:
    var gulp = require('gulp'); gulp.task('default', function() { // 将你的默认的任务代码放在这 });
  5. 测试一下,看是否OK了,直接执行gulp吧。
    以上的说明,来自官网Get Start

项目

我的项目是直播系统前端,主要功能是:

  • 直播主页
  • 列表页
  • 个人信息页
  • 直播页面,分Web/PC/Mobile/端,每个端页面不一样
  • PC聊天页面
  • 直播页面又分jwplayer7与jwplayer6的播放器
  • 再加上语音WebRTC功能

项目git地址在这里,纯前端代码在src/main/webapp/live目录中。

项目中已有package.json,checkout之后,直接执行npm install 会下载所有的依赖。然后再gulp会生成目标的html和js,这些文件可以用ftp直接放到tomcat的服务器中进行使用,请不要把这些文件用git上传。

至于CDN上传,可以走公司内部的CDN刷新功能上线就可以了。旧的文件可以不用管,所以不会有覆盖上线的问题。

代码

 
var gulp = require('gulp');

var concat = require('gulp-concat'); // 拼接工具
var uglify = require('gulp-uglify'); // 压缩工具
var rev = require('gulp-rev');       // 加md5后缀的工具
var revCollector = require('gulp-rev-collector'); //与rev共同使用
var useref = require('gulp-useref'); // 替换文件中的链接的工具,一般与gulp-rev共同使用
var minifyCss = require('gulp-minify-css'); // 压缩css工具
var del = require('del');
var gulpSequence = require('gulp-sequence');

// 任务处理的文件路径配置
var paths = {
    indexJs: [
        'compress/LivePlatform.js',
        'compress/playerConfig.js',
        'compress/chat.js',
        'compress/netOptimize.js',
        'compress/recordChat.js',
        'compress/question.js',
        'compress/liveEmotion.js',
        'compress/pageinfo.js'
    ],
    homeJs: [
        'compress/LivePlatform.js',
        'compress/home.js'
    ],
    liveListJs: [
        'compress/LivePlatform.js',
        'compress/liveList.js'
    ],
    myLiveJs: [
        'compress/LivePlatform.js',
        'compress/myList.js'
    ],
    homeMobileJs: [
        'compress/LivePlatform.js',
        'compress/homeMobile.js'
    ],
    webviewJs: [
        'compress/LivePlatform.js',
        'compress/liveEmotion.js',
        'compress/app.js',
        'compress/chat.js'
    ],

    indexHtml:'html/index*.html',
    liveMobileHtml:'html/liveMobile.html',
    livePCHTML:'html/livePC.html',
    homeHtml:'html/home.html',
    liveListHtml:'html/liveList.html',
    myLiveHtml:'html/myLive.html',
    homeMobileHtml:'html/homeMobile.html',
    webviewHtml:'html/webview.html'
};

/* 进行清理 */
gulp.task('clean', function () {
    return del([
        'dist/**/*',
        'rev*',
        'compress',
        '*.html',
        '*-*.js',
        ], function(){});
});

/* 进行压缩 */
gulp.task('compress', function () {
    //先进行压缩
    return gulp.src(['js/*.js', 'js/home/*.js'])
        .pipe(uglify())
        .pipe(gulp.dest('./compress'));
});


/* 进行压缩与合并,然后加md5文件后缀 */
gulp.task('concat', function() {
    //处理index.html, index7.html, liveMobile.html的引用
    gulp.src(paths.indexJs)
        .pipe(concat('index.js'))
        .pipe(rev())
        .pipe(gulp.dest('.'))
        .pipe(rev.manifest())
        .pipe(gulp.dest('./revIndex/'));

     //处理home.html
    gulp.src(paths.homeJs)
        .pipe(concat('home.js'))
        .pipe(rev())
        .pipe(gulp.dest('.'))
        .pipe(rev.manifest())
        .pipe(gulp.dest('./revHome/'));

    //处理liveList.html
    gulp.src(paths.liveListJs)
        .pipe(concat('liveList.js'))
        .pipe(rev())
        .pipe(gulp.dest('.'))
        .pipe(rev.manifest())
        .pipe(gulp.dest('./revLiveList/'));

    //处理myLive.html
    gulp.src(paths.myLiveJs)
        .pipe(concat('myLive.js'))
        .pipe(rev())
        .pipe(gulp.dest('.'))
        .pipe(rev.manifest())
        .pipe(gulp.dest('./revMyLive/'));

    //处理homeMobile.html
    gulp.src(paths.homeMobileJs)
        .pipe(concat('homeMobile.js'))
        .pipe(rev())
        .pipe(gulp.dest('.'))
        .pipe(rev.manifest())
        .pipe(gulp.dest('./revHomeM/'));

    //处理webview.html
    return gulp.src(paths.webviewJs)
        .pipe(concat('webview.js'))
        .pipe(rev())
        .pipe(gulp.dest('.'))
        .pipe(rev.manifest())
        .pipe(gulp.dest('./revWebview/'));
});

gulp.task('modifyRef', function() {
    //处理index.html,index7.html,liveMobile,
    //替换html中是js引用
    gulp.src(['./revIndex/rev-manifest.json', paths.indexHtml])//用rev-manifest.json的模式来修改html文件
        .pipe(revCollector())
        .pipe(gulp.dest('.'));
    gulp.src(['./revIndex/*.json', paths.liveMobileHtml])
        .pipe(revCollector())
        .pipe(gulp.dest('.'));
    gulp.src(['./revIndex/*.json', paths.livePCHTML])
        .pipe(revCollector())
        .pipe(gulp.dest('.'));

    //处理home.html
    gulp.src(['./revHome/*.json', paths.homeHtml])
        .pipe(revCollector())
        .pipe(gulp.dest('.'));
    //处理liveList.html
    gulp.src(['./revLiveList/*.json', paths.liveListHtml])
        .pipe(revCollector())
        .pipe(gulp.dest('.'));
    //处理myLive.html
    gulp.src(['./revMyLive/*.json', paths.myLiveHtml])
        .pipe(revCollector())
        .pipe(gulp.dest('.'));
    //处理homeMobile.html
    gulp.src(['./revHomeM/*.json', paths.homeMobileHtml])
        .pipe(revCollector())
        .pipe(gulp.dest('.'));
    //处理webview.html
    return gulp.src(['./revWebview/*.json', paths.webviewHtml])
        .pipe(revCollector())
        .pipe(gulp.dest('.'));
});


gulp.task('test', function() {
    //处理index.html,index7.html,liveMobile,
    //替换html中是js引用
    gulp.src(['./revIndex/*.json','html/index.html'])//用rev-manifest.json的模式来修改html文件
        .pipe(revCollector())
        .pipe(gulp.dest('.'));
});

gulp.task('build', gulpSequence('clean', 'compress', 'concat'));
gulp.task('release', gulpSequence('modifyRef'));
gulp.task('default', ['release']);

注意点

  1. package.json中有所有的引用,直接在项目目录下面npm install 就可以安装好了。
  2. 一个大坑,可能是我不太会用的原因吧,任务的执行顺序是很重要的: 如果不在task中有return,会出问题; 用gulp接口中的task依赖会出问题,例如gulp.task('release', ['compress'], function() {},另外就算用了gulp-sequenc这样的工具,如果涉及到写文件,也是有可能之前的文件还没有写好,但后面要用,依然会失败。这也是为什么我在release task里面把build和release分开的原因。按常理来说是没有问题的,但我反复实验,确实会失败.
    另外听说gulp4.0中本身带顺序执行了,不知道是否还有这个坑。
  3. 我的代码写得还是复杂了一些,每一个html所依赖的js是分别压缩的,但这样修改起来比较方便。
  4. 至于grunt,反正现在主流也是用gulp来替代它了,所以用不用也就无所谓了。
  5. gulp.dest('dir')只是把src中的以通配符或者最后的文件名写入到dir目录中而不是dir文件中。所以对于html来说,原文件名最好和要发布的就是同样的,否则还需要做rename操作,还得有依赖前一个写文件的操作已经结束。所以同名最简单。
  6. 上面的代码是确实可执行的,有编译问题可以找我。css我还没有处理,不过应该类似,可以自行处理。代码中有冗余,例如可以做成一个方法,另外后续有代码检查或者一键发布的功能了,后续再在这里补全。

资料

  1. 官网
  2. 官方文档
  3. gulp官方插件搜索列表
  4. 别的包,可以到这里找npmjs,里面的例子都很容易做实验的.
comments powered by Disqus