前端开发工具集(六):任务构建工具(grunt,gulp,npm script)

1,183 阅读3分钟

本文是开发工具集系列文章之一,其他请点击这里


1 概述

任务构建工具(task-runner)就是一些自动化组织、执行任务的工具,用来执行一些命令或利用插件处理文件,比如压缩、转换、格式化等,比较传统的工具包括gulp,grunt,还有npm自带的npm script。

如果我们选择一个合适的打包工具,比如webpack,这些打包工具会覆盖大部分任务构建工具的功能,再加上npm script,大部分场景不再需要其他构建工具了。但是这里还是会对这三种构建工具做一下介绍以作了解。

2 grunt和gulp

目前gulp的使用数量明显多于grunt,gulp是在保留grunt精华的基础上做了改进,甚至能使用后者的插件
gulp官网的介绍,我们基本能了解gulp在grunt基础上做了哪些事

  • 用提供的node.js api使用,而不是grunt一样重配置
  • 编写功能单一的任务进行组合,而不是一个任务完成多个事情,其中的插件也是专注于特定功能
  • 在文件最终输出前使用内存中的流,而不是输出中间文件

下面具体介绍一下两者的用法

2.1 grunt

grunt的使用通过配置来指定,配置文件为Gruntfile.js,当用命令行调用grunt时就会读取对应配置文件,一个配置文件包含以下四部分

  • wrapper函数,即导出的整个函数
  • 项目和任务配置,使用grunt.initConfig初始化一个配置.该配置对象会在后续任务中通过grunt.config访问
  • 加载插件和任务,常见的任务以插件形式封装,可以使用grunt.loadNpmTasks加载使用。
  • 自定义插件和任务,可以自定义默认的任务,可以是下例中插件封装的任务,也可以是自定义任务,比如
module.exports = function(grunt) {

  // A very basic default task.
  grunt.registerTask('default', 'Log some stuff.', function() {
    grunt.log.write('Logging some stuff...').ok();
  });

};

完整的配置文件如

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
      options: {
        banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
      },
      build: {
        src: 'src/<%= pkg.name %>.js',
        dest: 'build/<%= pkg.name %>.min.js'
      }
    }
  });

  // 加载包含 "uglify" 任务的插件。
  grunt.loadNpmTasks('grunt-contrib-uglify');

  // 默认被执行的任务列表。
  grunt.registerTask('default', ['uglify']);

};

更多请参考官方文档

2.2 gulp

使用gulpfile.js作为配置文件,其中包含执行的task,每个task是一个异步的js函数,接收一个error first回调或返回一个流、promise、event emiter、child process or observable
任务分private和public,前者只能在配置文件内部调用,后者可以在外调用。多个任务可以使用series() and parallel()进行组合,前者顺序执行,后者并发执行

const { series } = require('gulp');

// The `clean` function is not exported so it can be considered a private task.
// It can still be used within the `series()` composition.
function clean(cb) {
  // body omitted
  cb();
}

// The `build` function is exported so it is public and can be run with the `gulp` command.
// It can also be used within the `series()` composition.
function build(cb) {
  // body omitted
  cb();
}

exports.build = build;
exports.default = series(clean, build);

当处理一个文件时。在src() and dest()方法间使用.pipe()可以改变文件的内容,此时便可以借助插件

const { src, dest } = require('gulp');
const uglify = require('gulp-uglify');
const rename = require('gulp-rename');

exports.default = function() {
  return src('src/*.js')
    // The gulp-uglify plugin won't update the filename
    .pipe(uglify())
    // So use gulp-rename to change the extension
    .pipe(rename({ extname: '.min.js' }))
    .pipe(dest('output/'));
}

3. npm srcipt

前面我们熟悉了grunt和gulp的工作模式,它们存在以下问题

  • 需要借助插件处理,其生态与npm script所需包相比不够完善
  • 任务构建语法过于繁琐,不利于debug

再加上npm的普及,现在npm script+打包工具的组合对上述工具的优势已经十分明显。现在对npm script相关功能做一下讨论,参考官方文档
npm script的脚本位于package.json的scripts字段。

其中的命令使用npm run 执行,对应的premyscript和postmyscript也会先后执行,比如

{
  "scripts": {
    "precompress": "{{ executes BEFORE the `compress` script }}",
    "compress": "{{ run command to compress files }}",
    "postcompress": "{{ executes AFTER `compress` script }}"
  }
}

还可以使用&& 一次性执行多个命令或者互相调用

"scripts": {
  ...
  "build:images": "npm run imagemin && npm run icons",
  "build:all": "npm run build:css && npm run build:js && npm run build:images",
}

完结撒花