Front end-"Gulp packaging execution principle and detailed explanation of some plug-ins

Gulp is a very popular front-end automated construction tool, which is mainly used to set programs to automatically handle static resources. Simply put, gulp is used to package projects.

First look at my front-end project structure, it should be the same as most people's project structure:

As shown in FIG: 

  • src directory: code directory
  • bower_components directory: static js directory
  • build directory: the compilation directory of the src directory, which can be deleted and regenerated after compilation
  • node_modules directory: fixed directory of the node environment

 The main directories discussed in this article:

  • Package file: gulpfile.js 

The main content of the package file:

var gulp = require('gulp');
var connect = require('gulp-connect');/*创建本地服务器*/
var modRewrite = require('connect-modrewrite');
var wiredep = require('wiredep').stream;
var usemin = require('gulp-usemin');
var uglify = require('gulp-uglify');/*最小化js文件*/
var minifyHtml = require('gulp-minify-html');
var minifyCss = require('gulp-minify-css');/*最小化css文件*/
var rev = require('gulp-rev');
var clean = require('gulp-clean');
var concat = require('gulp-concat');/*合并文件*/
var copy = require('gulp-copy');/*拷贝文件*/
var argv = require('yargs').argv;
var watch = require('gulp-watch');
var fs = require('fs-sync');
var jshint = require('gulp-jshint');
var map = require('map-stream');
var less = require('gulp-less');/*获取gulp-less插件*/
var replace = require('gulp-replace');
var rename = require('gulp-rename');
var revCollector = require('gulp-rev-collector');

if (argv.env === undefined) {
  console.error("Please specify a profile, like `--env staging`. Refer to README.md for more details.");
  process.exit();
}

/*定义一个静态集合,主要包含了几个js的路径。这个集合在jsLib这个任务中用到*/
var jsLibList = [
  'bower_components/jquery/dist/jquery.min.js',
  'bower_components/jquery.cookie/jquery.cookie.js',
  "bower_components/base-64/base64.js",
  "bower_components/clipboard/dist/clipboard.min.js",
  "bower_components/js-md5/build/md5.min.js"
];


/**
 * 启动一个Web服务器
 * 并且开启 rewrite 功能,配合html5mode
 * gulp.task是用来创建一个任务(相当于创建一个方法,类似function关键字)
 * gulp.task有3个参数,gulp.task('name3',['name1','name2'],fn)
 * 第一个参数name3是当前任务的任务名,类似方法名
 * 第三个参数,fn,是当前名为name3的这个任务具体的代码,可省略。
 * 第二个参数['name1','name2']是依赖的其他方法的方法名的集合,
 *    如果定义了这第二个参数,则表示当前名为name3的这个任务需要先执行名为name1和name2的任务之后,才能其自身的代码fn。
 *    如果未定义这第二个参数,则直接执行自身代码fn
 *    如果定义了这第二个参数,未定义其自身执行代码fn,则当前这个名为name3的任务只相当于同时调用了 name1和name2的方法,相当于将原本的子任务全部集合到一个主任务上面来,方便管理。
 */

/*创建一个名为websever的任务,主要用于开启本地服务*/
gulp.task('webserver', function() {
  /*创建本地连接并,port:端口号,livereload:开启实时刷新,debug:开启调试*/
  connect.server({
    port: 8001,
    livereload: true,
    debug: true
  });
});

//build wap
/*gulp.task用来创建一个名为build的任务,主要用于集合编译并删除多余文件
第一个参数build,是创建的任务名,即方法名,
第二个参数是 其他任务名的集合,相当于把其他任务名对应的方法放到一起执行,即同时调用这个了5个方法
这个方法相当于把build_index_and_js,images,lib,json和cleanSurplus这5个方法集合起来执行,形成了一个新的方法,这个新的方法的方法名叫build*/
gulp.task('build', ['build_index_and_js', 'images', 'lib','json','cleanSurplus']);


/*gulp.task用来创建一个名为watch的任务,主要用于关联和运行这个gulpfile.js文件中定义的其他任务
第一个参数watch是方法名,是创建的任务名,即方法名,
第二个参数是 其他任务名的集合,相当于把build和webserver两个方法放到一起执行
第三个参数是 watch本身的执行代码,表示在执行完build和webserver方法后,
   第三个参数的具体代码即gulp.watch的第一个参数是路径集合,第二个参数是任务集合
   表示当第一个参数中的文件内容发生变化的时候,就会再重新调用执行一次第二个参数中任务名所对应的任务,
   eg:如果修改了src/activities/collection/index.html修改时,会先执行build_index_and_js方法,再执行cleanSurplus方法删除多余文件*/
gulp.task('watch', ['build', 'webserver'], function() {
  gulp.watch(['src/js/**/*.js', 'src/app.js', 'src/activities/**/*.html', 'src/css/**/*.less'], ['build_index_and_js', 'cleanSurplus']);
});

gulp.task('lint', function() {
  return gulp.src(['src/js/**/*.js', '!src/js/config.js'])
      .pipe(jshint())
      .pipe(jshint.reporter('default'));
});

/*创建一个名为clean的任务,主要用于删除文件*/
gulp.task('clean', function() {
  /*gulp.src第一个参数:是扫描build及其子目录和子文件,第二个参数{read:false}:是不读取文件,加快程序
  *扫描完成后执行clean()方法,可以看到clean方法实际调用的是:gulp-clean插件,即删除扫描到的文件和文件夹*/
  return gulp.src('build/', { read: false })
      .pipe(clean());
});

/*创建一个名为images的任务,主要用于拷贝src/images/路径下的静态资源*/
gulp.task('images', function() {
  /*gulp.src:扫描src/images/路径下的文件和文件夹
  *扫描完成后执行copy()方法,可以看到copy方法实际调用的是gulp-copy插件,即将扫描到的文件及其路径拷贝到build目录下*/
  return gulp.src(['src/images/**'])
      .pipe(copy('build/', { prefix: 1 })); // prefix = 1, ignore src dir
});

/*创建一个名为json的任务,主要用于拷贝src/data/路径下的静态资源*/
gulp.task('json', function() {
  /*gulp.src:扫描src/data/路径下的文件和文件夹
   *同上,将扫描到的文件及其路径容拷贝到build目录下*/
  return gulp.src(['src/data/**'])
      .pipe(copy('build/', { prefix: 1 })); // prefix = 1, ignore src dir
});

/*创建一个名为cleanWatchBuild的任务,主要用于删除已经编译过的html、css和js文件*/
gulp.task('cleanWatchBuild', function() {
  /*gulp.src:扫描'build/css', 'build/js', 'build/activities'这三个路径下的文件和文件夹
  * 扫描完成后调用clean()方法,可以看到clean方法实际调用的是:gulp-clean插件,即删除扫描到的文件和文件夹*/
  return gulp.src(['build/css', 'build/js', 'build/activities'])
      .pipe(clean());
});

/*创建一个名为txtCopy的任务,主要用于拷贝txt或html文件*/
gulp.task('txtCopy', function() {
  /*gulp.src:扫描所有后缀名为txt和html的文件
  *同上,将扫描到的文件及其路径拷贝到build目录下 */
  return gulp.src(['*.txt','*.html'])
      .pipe(copy('build/'));
});

/*创建一个名为css的任务,先调用名为cleanWatchBuild和txtCopy的任务,主要用于编译css文件。
将src目录下的所有的less样式文件转成css,随后压缩并合并成一个名为app.css的文件,对这个文件加上md5版本签名,
生成到build/css路径下,并生成映射文件放到src/css*/
gulp.task('css', ['cleanWatchBuild', 'txtCopy'], function() {
  /*gulp.src:扫描src/css目录下所有的less文件,并通过pipe转成文件流
  * less():调用gulp-less插件:将less文件编译成css文件
  * minifyCss():调用gulp-minify-css插件:将css文件压缩,减少文件大小
  * concat('app.css'):调用gulp-concat插件:将多个css文件合并到一个临时文件app.css
  * rev():调用gulp-rev插件:根据文件内容,生成md5签名,加在文件名上,不会因为缓存问题,页面无法立即刷新。当文件发生变化后,md5变化,文件名发生变化,浏览器读取新文件,从而解决缓存问题
  * gulp.dest('build/css'):调用gulp自带的api即dest方法,将经过上面一系列操作的文件流进行输出生成文件放到build/css路径下
  *rev.manifest:调用gulp-rev插件的manifest方法:将app.css的文件名与其 版本号(即md5签名)对应输出到js映射文件,保存到src下的rev-manifest.json文件中,
  *               其中base为存放上一个生成的rev-manifest.json文件,然后merge为是否在已有文件后面追加,由于在当前任务之前没有映射输出,所以直接创建一个名为rev-manifest.json的文件
  *               最终生成的rev-manifest.json文件内容就是{"app.css": "app-44e278f711.css"}*/
  return gulp.src(['src/css/**/*.less'])
      .pipe(less())
      .pipe(minifyCss())
      .pipe(concat('app.css'))
      .pipe(rev())
      .pipe(gulp.dest('build/css'))
      .pipe(rev.manifest({
        base: 'src/**',
        merge: true
      }))
      .pipe(gulp.dest("src/css"));
});

/*创建一个名为jsApp的任务,先调用名为css的任务。主要用于编译src目录下的js文件*/
gulp.task('jsApp', ['css'], function() {
  /*gulp.src:扫描参数集合中包含的指定js和指定路径下的js文件,转成流
  *uglify():调用gulp-uglify插件:压缩扫描到的文件流
  *concat('app.js'):调用gulp-concat插件:将上一步处理后的文件流合并成一个文件app.js
  *rev():调用gulp-rev插件:给上一步处理后的文件名上加上MD5签名,使得浏览器读取这次生成的新文件,以避免出现缓存问题
  *gulp.dest:调用gulp自带的api即dest方法 ,将文件输出到指定目录下
  *rev.manifest:调用gulp-rev插件的manifest方法:将app.js与其 版本号(即md5签名)对应输出到js映射文件,保存到src下的rev-manifest.json文件中,
  *               其中base为存放上一个生成的rev-manifest.json文件,然后merge为是否在已有文件后面追加,
  *               比如当前任务调用了css这个任务,css任务在rev-manifest.json中输出了映射关系"app.css": "app-44e278f711.css",那么追加则为在这个关系后面添加一个如:"app.js": "app-ef93c0cc1c.js"
  *               最终生成的rev-manifest.json文件就是{"app.css": "app-44e278f711.css","app.js": "app-ef93c0cc1c.js"}*/
  return gulp.src(['src/config.js', 'src/js/app.js', 'src/js/service/*.js', 'src/js/util/*.js'])
      .pipe(uglify())
      .pipe(concat('app.js'))
      .pipe(rev())
      .pipe(gulp.dest('build/js/'))
      .pipe(rev.manifest({
        base: 'src/**',
        merge: true
      }))
      .pipe(gulp.dest("src/js"));
});

/*创建一个名为jsLib的任务,先调用名为jsApp的任务。主要用于编译bower_components目录下的js文件,输出为lib.js*/
gulp.task('jsLib', ['jsApp'], function() {
  /*gulp.src:扫描参数集合中包含的指定js,转成流
  *uglify():调用gulp-uglify插件:压缩扫描到的文件流
  *concat('lib.js'):调用gulp-concat插件:将上一步处理后的文件流合并成一个文件lib.js
  *rev():调用gulp-rev插件:给上一步处理后的文件名上加上MD5签名,使得浏览器读取这次生成的新文件,以避免出现缓存问题
  *gulp.dest:调用gulp自带的api即dest方法 ,将文件输出到指定目录下
  *rev.manifest:调用gulp-rev插件的manifest方法:将lib.js与其 版本号(即md5签名)对应输出到js映射文件,保存到src下的rev-manifest.json文件中,
  *               其中base为存放上一个生成的rev-manifest.json文件,然后merge为是否在已有文件后面追加,比如当前任务调用了jsApp这个任务,jsApp任务在rev-manifest.json中输出了映射关系  "app.js": "app-ef93c0cc1c.js",,那么追加则为在这个关系后面添加一个如:"lib.js": "lib-8f9133ddc7.js"
  *               最终生成的rev-manifest.json文件就是{"app.css": "app-44e278f711.css","app.js": "app-ef93c0cc1c.js","lib.js": "lib-8f9133ddc7.js"}*/
  return gulp.src(jsLibList)
      .pipe(uglify())
      .pipe(concat('lib.js'))
      .pipe(rev())
      .pipe(gulp.dest('build/js/'))
      .pipe(rev.manifest({
        base: 'src/**',
        merge: true
      }))
      .pipe(gulp.dest("build/lib"));
});

/*创建一个名为jsController的任务,先调用名为jsLib的任务。主要用于编译controller文件*/
gulp.task('jsController', ['jsLib'], function() {
  /*gulp.src此处扫描的controller文件夹似乎不存在,有可能是路径写错了,暂时不做修改
  * 下面步骤跟上一个jsLib的执行步骤一抹一样,不做赘述*/
  return gulp.src(['src/js/controller/**/*.js'])
      .pipe(uglify({}))
      .pipe(rev())
      .pipe(gulp.dest('build/js/controller'))
      .pipe(rev.manifest({
        base: 'src/**',
        merge: true
      }))
      .pipe(gulp.dest("build/jsController"));
});

/*创建一个名为rev的任务,先调用名为jsController的任务。主要用于根据上一步生成的rev-manifest.json文件中的映射关系,替换调html中的资源文件名称*/
gulp.task('rev', ['jsController'], function() {
  /*gulp.src:扫描rev-manifest.json映射文件和所有src下的html文件
  * revCollector({replaceReved: true,dirReplacements:{"/":"/"}}): 调用gulp-rev-collector插件,将扫描到的所有html文件中携带md5签名的静态资源的资源名称
  *               按照rev-manifest.json中对应的关系,替换为最新生成的md5签名。
  *               replaceReved: 用来说明模板中已经被替换的文件是否还能再被替换
  *               dirReplacements:路径替换*/
  return gulp.src(
      [
        'rev-manifest.json',
        'src/**/*.html',
      ])
      .pipe(revCollector({
        replaceReved: true,
        dirReplacements: {
          '/': '/',
        }
      }))
      .pipe(gulp.dest('build'));
});

/*创建一个名为cleanSurplus的任务,用来将md5签名替换完成后留下来的rev-manifest.json等临时文件删掉,任务的执行顺序是 先替换再删除*/
gulp.task('cleanSurplus', ['rev'], function() {
    /*gulp.src:扫描到这两个临时文件,不读取文件内容
    * clean():调用gulp-clean插件 删除扫描到的文件*/
    return gulp.src(['rev-manifest.json', 'src/config.js'], { read: false })
        .pipe(clean());
});

/*创建一个名为 lib的任务,复制src/lib文件夹到build文件夹下*/
gulp.task('lib', function() {
  return gulp.src(['src/lib/**'])
      .pipe(copy('build/', { prefix: 1 }));
});

/*创建一个名为build_index_and_js的任务,主要用来根据命令行参数判断启动的是正式环境还是测试环境*/
gulp.task('build_index_and_js', function() {
  /*将正式环境、测试环境和开发环境的路径定义到map中
  * argv.env:调用argv的env参数 获取命令行中设置的env的值。在命令行中输入env dev,就相当于给env这个key设置了value=dev
  * fs.copy:调用fs-sync插件的copy方法,同步的将configFile变量指定的路径下的文件复制到src下的config文件,如果config.js已经存在,则覆盖它*/
  var envConfigMap = {
    'production': 'src/config.production.js',
    'staging': 'src/config.staging.js',
    'dev': 'src/config.dev.js'
  };
  var configFile = envConfigMap[argv.env];
  fs.copy(configFile, 'src/config.js');
});

 I believe that after reading the very, very detailed comments above, I have a clear understanding of the execution principle, plug-in function and execution order of gulp.

 Then we start the project:

  •  Go to the project directory, open the command line and enter: gulp watch --env dev

(The popular meaning of the gulp watch --env dev command is: call the task named watch built in gulp, and set a parameter, the name of the parameter is env, and the value of the parameter is dev. eg: When there is a method definition : Watch(env){}, when calling: this.watch("dev")) 

 After the above command is executed, the program will start to compile. During the compilation process, the build directory will be automatically generated according to the src directory. After the compilation is completed, the default browser will be automatically opened to display the page.

 

Guess you like

Origin blog.csdn.net/nienianzhi1744/article/details/107841950