前端自动化构建工具 gruntgulp的使用
begin enjoy👇

一、 Grunt

1.grunt简介

grunt是一个基于nodeJS的命令行工具,通过其丰富而强大的插件执行一系列与前端自动化构建相关任务,例如:文件合并、压缩、脚本验证、less和sass预编译等。使用grunt可以实现项目更可靠的运行,有效提升项目部署的效率。

grunt基于nodejs,它使用CMD的模块加载机制并且使用npm库中的grunt插件,所有grunt生态插件都以包的形式发布到了npm平台。grunt插件包以grunt-contrib-grunt-开头,前者为grunt官方发布的插件包,后者则为第三方用户编写的包。

使用前检查项目环境中是否有安装的node环境:node -v

2.快速入门

使用grunt前,需创建一个简单的测试用例:grunt_app。目录文件结构如下:

|- dist----------目标文件夹
|- src------------源码文件夹   
    |- js---------------js源文件夹
    |- css--------------css源文件夹
|- index.html-----测试文件
|- Gruntfile.js---grunt配置文件(注意首字母大写)
|- package.json---项目包配置文件

1) 全局安装 grunt-cli

你需要先将Grunt命令行(CLI)安装到全局环境中。安装时可能需要使用sudo(针对OSX、*nix、BSD等系统中)权限或者作为管理员(对于Windows环境)来执行以下命令:

npm install -g grunt-cli

上述命令执行完后,grunt 命令就被加入到你的系统路径中了,以后就可以在任何目录下执行此命令了。
注意,安装grunt-cli并不等于安装了 Grunt!Grunt CLI的任务很简单:调用与Gruntfile在同一目录中的Grunt。这样带来的好处是允许你在同一个系统上同时安装多个版本的 Grunt。

每次运行grunt 时,他就利用node提供的require()系统查找本地安装的 Grunt。正是由于这一机制,你可以在项目的任意子目录中运行grunt
如果找到一份本地安装的 Grunt,CLI就将其加载,并传递Gruntfile中的配置信息,然后执行你所指定的任务。

2) 本地安装grunt

本地安装grunt,并运行构建项目命令。初始化运行grunt指令,会提示Warning: Task "default" not found。需要在根地址中添加Grantfile.js文件并配置default任务。

npm install grunt --save-dev
grunt

3) Gruntfile文件

Gruntfile.jsGruntfile.coffee 文件是有效的 JavaScript 或 CoffeeScript 文件,应当放在你的项目根目录中,和package.json文件在同一目录层级,并和项目源码一起加入源码管理器。
Gruntfile由以下几部分构成:

  • "wrapper" 函数

  • 项目与任务配置

  • 加载grunt插件和任务

  • 自定义任务
    在下面列出的这个 Gruntfile 中,package.json文件中的项目元数据(metadata)被导入到 Grunt 配置中, grunt-contrib-uglify 插件中的uglify 任务(task)被配置为压缩(minify)源码文件并依据上述元数据动态生成一个文件头注释。当在命令行中执行 grunt 命令时,uglify 任务将被默认执行。

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']);

};

前面已经向你展示了整个 Gruntfile,接下来将详细解释其中的每一部分。

"wrapper" 函数

每一份 Gruntfile (和grunt插件)都遵循同样的格式,你所书写的Grunt代码必须放在此函数内:

module.exports = function(grunt) {
  // Do grunt-related things in here
};

项目和任务配置

大部分的Grunt任务都依赖某些配置数据,这些数据被定义在一个object内,并传递给grunt.initConfig 方法。

在下面的案例中,grunt.file.readJSON('package.json') 将存储在package.json文件中的JSON元数据引入到grunt config中。 由于<% %>模板字符串可以引用任意的配置属性,因此可以通过这种方式来指定诸如文件路径和文件列表类型的配置数据,从而减少一些重复的工作。

你可以在这个配置对象中(传递给initConfig()方法的对象)存储任意的数据,只要它不与你任务配置所需的属性冲突,否则会被忽略。此外,由于这本身就是JavaScript,你不仅限于使用JSON;你可以在这里使用任意的有效的JS代码。如果有必要,你甚至可以以编程的方式生成配置。

与大多数task一样,grunt-contrib-uglify 插件中的uglify 任务要求它的配置被指定在一个同名属性中。在这里有一个例子, 我们指定了一个banner选项(用于在文件顶部生成一个注释),紧接着是一个单一的名为build的uglify目标,用于将一个js文件压缩为一个目标文件。

// 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'
    }
  }
});

加载 Grunt 插件和任务

concatenation、[minification]、grunt-contrib-uglifylinting这些常用的任务(task)都已经以grunt插件的形式被开发出来了。只要在 package.json 文件中被列为dependency(依赖)的包,并通过npm install安装之后,都可以在Gruntfile中以简单命令的形式使用:

// 加载能够提供"uglify"任务的插件。
grunt.loadNpmTasks('grunt-contrib-uglify');

注意: grunt --help 命令将列出所有可用的任务。

自定义任务

通过定义 default 任务,可以让Grunt默认执行一个或多个任务。在下面的这个案例中,执行 grunt 命令时如果不指定一个任务的话,将会执行uglify任务。这和执行grunt uglify 或者 grunt default的效果一样。default任务列表数组中可以指定任意数目的任务(可以带参数)。

// Default task(s).
grunt.registerTask('default', ['uglify']);

如果Grunt插件中的任务(task)不能满足你的项目需求,你还可以在Gruntfile中自定义任务(task)。例如,在下面的 Gruntfile 中自定义了一个default 任务,并且他甚至不依赖任务配置:

module.exports = function(grunt) {

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

};

特定于项目的任务不必在 Gruntfile 中定义。他们可以定义在外部.js 文件中,并通过grunt.loadTasks 方法加载。

3.插件介绍

  • grunt官网的插件列表页面 http://www.gruntjs.net/plugins
  • 插件分类:
    • grunt团队贡献的插件 : 插件名大都以contrib-开头
    • 第三方提供的插件 : 大都不以contrib-开头
  • 常用的插件:
    • grunt-contrib-clean——清除文件(打包处理生成的)
    • grunt-contrib-concat——合并多个文件的代码到一个文件中
    • grunt-contrib-uglify——压缩js文件
    • grunt-contrib-jshint——javascript语法错误检查;
    • grunt-contrib-cssmin——压缩/合并css文件
    • grunt-contrib-htmlmin——压缩html文件
    • grunt-contrib-imagemin——压缩图片文件(无损)
    • grunt-contrib-copy——复制文件、文件夹
    • grunt-contrib-requirejs——合并压缩requirejs管理的所有js模块文件
    • grunt-contrib-watch——实时监控文件变化、调用相应的任务重新执行

4) 合并js: concat

  • 命令:

    npm install grunt-contrib-concat --save-dev
    
  • 编码:

    • src/js/test1.js

      (function () {
        function add(num1, num2) {
          return num1 + num2;
        }
        console.log(add(10, 20));
      })();
      
  • src/js/test2.js

    (function () {
      var arr = [2,3,4].map(function (item, index) {
        return item+1;
      });
      console.log(arr);
    })();
    }
    
    • 配置: Gruntfile.js

      • 配置任务:

        concat: {
            options: { //可选项配置
              separator: ';'   //使用;连接合并
            },
            build: { //此名称任意
              src:  ["src/js/*.js"],  //合并哪些js文件
              dest: "build/js/built.js" //输出的js文件
            }
          }
        
  • 加载插件:

    grunt.loadNpmTasks('grunt-contrib-concat');
    
    • 注册任务:

      grunt.registerTask('default', ['concat']);
      
    • 命令:

    grunt   //会在build下生成一个built.js
    

5) 压缩js: uglify

  • 下载

    npm install grunt-contrib-uglify --save-dev
    
  • 配置: Gruntfile.js

    • 配置任务:

      pkg : grunt.file.readJSON('package.json'),
      uglify : {
        options: {  //不是必须的
          banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
          '<%= grunt.template.today("yyyy-mm-dd") %> */'
        },
        build: {
          files: {
            'build/js/built-<%=pkg.name%>-<%=pkg.version%>.min.js': ['build/js/built.js']
          }
        }
      }
      
  • 加载任务:

    grunt.loadNpmTasks('grunt-contrib-uglify');
    
    • 注册任务:

      grunt.registerTask('default', ['concat', 'uglify']);
      
    • 命令:

    grunt   //会在build下生成一个压缩的js文件
    

6) js语法检查: jshint

  • 命令:

    npm install grunt-contrib-jshint --save-dev
    
  • 编码: .jshintrc

    {
      "curly": true,
      "eqeqeq": true,
      "eqnull": true,
      "expr" : true,
      "immed": true,
      "newcap": true,
      "noempty": true,
      "noarg": true,
      "regexp": true,
      "browser": true,
      "devel": true,
      "node": true,
      "boss": false,
      
      //不能使用未定义的变量
      "undef": true,
      //语句后面必须有分号
      "asi": false,
      //预定义不检查的全局变量
      "predef": [ "define", "BMap", "angular", "BMAP_STATUS_SUCCESS"]
    }
    
  • 修改src/js/test1.js

    (function () {
      function add(num1, num2) {
        num1 = num1 + num3
        return num1 + num2;
      }
      console.log(add(10, 20));
    })();
    
  • 配置 : Gruntfile.js

    • 配置任务:

      jshint : {
        options: {
          jshintrc : '.jshintrc' //指定配置文件
        },
        build : ['Gruntfile.js', 'src/js/*.js'] //指定检查的文件
      }
      
  • 加载任务:

    grunt.loadNpmTasks('grunt-contrib-jshint');
    
    • 注册任务:

      grunt.registerTask('default', ['concat', 'uglify', 'jshint']);
      
    • 命令:

    grunt   //提示变量未定义和语句后未加分号 -->修改后重新编译
    

7) 压缩css:cssmin

  • 安装:

    npm install grunt-contrib-cssmin --save-dev
    
  • 编码:

    • test1.css

      #box1 {
        width: 100px;
        height: 100px;
        background: red;
      }
      
  • test2.css

    #box2 {
      width: 200px;
      height: 200px;
      background: blue;
    }
    
    • index.html

      <link rel="stylesheet" href="build/css/output.min.css">
      <div id="box1"></div>
      <div id="box2"></div>
      
    • 配置 : Gruntfile.js

      • 配置任务:

        cssmin:{
          options: {
            shorthandCompacting: false,
            roundingPrecision: -1
          },
          build: {
            files: {
                'build/css/output.min.css': ['src/css/*.css']
            }
          }
        }
        
  • 加载任务:

    grunt.loadNpmTasks('grunt-contrib-cssmin');
    
    • 注册任务:

      grunt.registerTask('default', ['concat', 'uglify', 'jshint', 'cssmin']);
      
    • 命令:

    grunt    //在build/css/下生成output.min.css
    

8) watch自动化监听

  • 命令: npm install grunt-contrib-watch --save-dev

  • 配置 : Gruntfile.js

    • 配置任务:

      watch : {
        scripts : {
          files : ['src/js/*.js', 'src/css/*.css'],
          tasks : ['concat', 'jshint', 'uglify', 'cssmin'],
          options : {spawn : false}  
        }
      }
      
  • 加载任务:

    grunt.loadNpmTasks('grunt-contrib-watch');
    
    • 注册任务:

      grunt.registerTask('default', ['concat', 'uglify', 'jshint', 'watch']);
      改进:grunt.registerTask('myWatch', ['default','watch']);
      
    • 命令:

    grunt   //控制台提示watch已经开始监听, 修改保存后自动编译处理
    

二、 Gulp

4.快速入门

9) Node环境检测

如果你先前将 gulp 安装到全局环境中了,请执行 npm rm --global gulp 将 gulp 删除再继续以下操作。接下来检查node、npm、npx是否安装正确:

node -v
npm -v
npx -v

10) 搭建测试项目

|----dist 构建之后的代码,实际部署的线上环境
|----src 开发时的代码,适合阅读和修改
|----|----js
|----|----css
|----index.html

11) 安装gulp

  • npm install gulp-cli --global
  • npm install gulp --save-dev

12) 创建gulpfile文件

创建gulpfile.js文件测试gulp指令:
gulpfile 是项目目录下名为 gulpfile.js ,在运行 gulp 命令时会被自动加载。这个文件用来编写gulp程序,也就是自动化的具体任务。文件中可以包含纯JavaScript 代码或 Node 模块。

//任何导出的函数都将注册到 gulp 的任务(task)系统中
exports.default = function (cb) {
cb();  //每个任务是一个异步的javascript函数 需要cb标记状态
}

5.使用插件

13) Node转换流

Gulp是基于流的操作,每个Gulp插件都是一个Node转换流(Transform Streams)。Gulp封装了读取文件流和输出流到文件的方法,插件用来在过程中转化流。转换流的插件需要放置封装的pipe()(pipeline管道)方法中。

src(源文件).pipe(插件操作).pipe(插件操作).dest(输出目标)

  • src() 接受 glob参数,从文件系统中读取文件然后生成一个 Node 流(stream)。它将所有匹配的文件读取到内存中并通过流(stream)进行处理。由src()产生的流(stream)应当从任务(task)中返回并发出异步完成的信号,就如创建任务(task) 文档中所述。
  • pipe() 管道方法用于流转化的连接,通过传入插件操作数据流,每个插件应当只完成必要的工作。因此你可以把它们像构建块一样连接在一起。获得想要的结果可能需要把一组插件组合在一起使用。
  • dest() 接受一个输出目录作为参数,并且它还会产生一个 Node 流(stream),通常作为终止流(terminator stream)。当它接收到通过管道(pipeline)传输的文件时,它会将文件内容及文件属性写入到指定的目录中并且沿用之前的文件名。如果要重命名,需要在此之前使用rename插件修改。

14) 常用插件列表

托管在 npm 上的插件标记有 "gulpplugin" 和 "gulpfriendly" 关键词 - 可以在插件搜索页面上浏览和搜索。在项目开发中,通常需要对html,css和js进行处理。特别注意,当代码经过转化后,原有文件中的路径和名字要作同步的修改,否则无法执行。
Javascript相关插件

  • gulp-concat 合并多个javascript文件为一个整体,需要指定一个新的名称。gulp不支持es6的模块合并,使用export和import会出错。它可以通过插件合并优化requirejs加载的模块。

  • gulp-uglify 压缩javascript代码,不会重命名

  • gulp-rename 重命名文件,它支持对象形式的配置参数

    const gulp = require("gulp");
    	// console.dir(gulp);
    	
    //从模块提取方法
    const {src, dest} = gulp;
    
    //加载gulp插件包
    const concat = require("gulp-concat"),
      uglify = require("gulp-uglify"),
      rename = require("gulp-rename");
    
    //注册默认任务
    exports.default = function (cb) {
      //模块具体的执行动作,需要返回异步完成信号
      return src("./src/js/*.js")
        .pipe(concat("build.js"))   //临时文件名
        .pipe(uglify()) //压缩文件
        .pipe(rename({extname : ".min.js"}))  //重新命名
        .pipe(dest("./dist/js"));  //输出目标21	
    }
    

    CSS相关的插件

    • gulp-concat-css 合并多个css文件为一个整体

    • gulp-clean-css 压缩css代码,并设置兼容ie8

    • gulp-less 编译less代码文件,一般在合并压缩前编译less文件,然后统一的进行操作。

      const {src, dest, series, parallel} = require("gulp");
      const concatCss = require("gulp-concat-css"),
          cleanCss = require("gulp-clean-css"),
          rename = require("gulp-rename"),
          lessCss = require("gulp-less");
          
      //注册多个单一任务,可以使用module.exports
      function css() {
              return src("./src/css/*.css")
                  .pipe(concatCss("./css/build.css"))
                  .pipe(rename({extname : ".min.css"}))
                  .pipe(cleanCss({compatibility:"ie8"}))
                  .pipe(dest("./dist/"));
      
      }
      
      function less() { 
            return src("src/less/*.less")
                .pipe(lessCSS())
                .pipe(dest("./src/css")); //自动加载名称
      }
      //以同步的方式 顺序执行列表任务
      exports.default = series(less, css);
      

HTML相关插件

  • gulp-htmlmin 压缩html代码,它能够帮助你优化html标签,并去除不必要的空白和注释。

    function html() {
      return src("./src/html/*.html")
          .pipe(dest("./dist/html/"))
          .pipe(src("src/**/*.html"))
          .pipe(htmlmin({
              removeComments: true, //清除HTML注释
              collapseWhitespace: true, //压缩HTML
              collapseBooleanAttributes: true, //省略布尔属性的值
              removeEmptyAttributes: true, //删除所有空格作属性值
              removeScriptTypeAttributes: true, //删除<script>的type
              removeStyleLinkTypeAttributes: true, //删除<style>的type
              minifyJS: true, //压缩页面JS
              minifyCSS: true //压缩页面CSS
          }))
          .pipe(dest("./dist/"));17	
    }
    

图片相关插件

  • gulp-imagemin 压缩图片格式 Minify PNG, JPEG, GIF and SVG images减少图片体积,提高网络传输速率。

    //图片压缩任务
    function img() {
        return src("src/images/*")
            .pipe(imagemin({
            //优化等级默认为:3  取值范围:0-7 
            optimizationLevel: 5,
            //默认为false 无损压缩jpg图片
            progressive: true,
            //默认为false 隔行扫描gif进行渲染
            interlaced: true,
            //默认为false 多次优化svg直到完全优化
            // multipass: true
        }))
            .pipe(imagemin({
            progressive: true, //无损压缩JPG图片  
            svgoPlugins: [{
                removeViewBox: false
            }], //不移除svg的viewbox属性
            use: [pngquant()] //使用pngquant插件进行深度压缩  
        }))
          .pipe(dest("./dist/images/"));39		
    }
    

requirejs模块优化

  • gulp-gulp-requirejs-optimize 优化合并requirejs的模块文件,将依赖模块注入主模块中,以此减少当前页面的http请求次数,提高加载效率。optimize插件只支持es5,需要先将模块中包含的es6语法进行转化。在管道方法中调用该插件时需要配置模块名和路径的关联映射,替代原先在主模块中使用require.config()配置的信息:

    function js() {
        return src("./src/js/*.js")
            .pipe(requirejsOptimize({
            optimize: "none",
    
            paths: {
                //依赖模块的路径列表
                "jquery": "../../node_modules/jquery/dist/jquery.min",
                "header": "./header",
                "slide": "../../node_modules/lautin-slide/slide",
                "tab": "../../node_modules/lautin-tab/tab",
                "template": "../../node_modules/art-template/lib/template-web",
                "utils": "./utils",
                "jqzoom": "./jqzoom-core",
                "pagination": "../../node_modules/lautin-pagination/libs/Pagination"
            },
            //非规范模块设置
            shim: {
                'slide': {
                    deps: [], //依赖模块
                    exports: 'Slide' //导出接口
                },
                'tab': {
                    deps: [],
                    exports: 'Tab'
                },
                'pagination': {
                    deps: [], //依赖模块
                    exports: 'Pagination' //导出接口
                },
            },
            //不打包的静态文件
            excludeShallow: [
                'jquery', 'bootstrap', 'clockpicker', 'datetimepickerLang', 'clipboard', 'moment', 'datetimepicker', 'daterangepicker'
            ],
            preserveLicenseComments: false, //去掉头部版权声明
            removeCombined: false, //自动删除被合并过的文件
        }))
            .pipe(uglify()) //压缩
            .pipe(rename({ //重命名
            extname: ".min.js"
    }))
            .pipe(dest("./dist/js/")); //输出	}
    
  • gulp-babel javascript代码生成器,可以转化代码以及Polyfill 使用时需保证加载es6规范。接下来在管道方法中调用插件时添加配置presets。

cnpm install --save-dev gulp-babel@7 babel-core babel-preset-env
cnpm install babel-preset-es2015
babel({presets: ['es2015']})
  • gulp-load-plugins 自动加载插件,无需手动引入 但前提需要先下载

6.非插件应用

并非 gulp 中的一切都需要用插件来完成。虽然它们是一种快速上手的方法,但许多操作都应当通过使用独立的功能模块或库来实现。
插件应当总是用来转换文件的。其他操作都应该使用(非插件的) Node 模块或库来实现。

//并非基于插件,所有的node模块都支持
const fs = require("fs");
exports.default = function (cb) {
    //所有可执行的node代码都可以
    fs.unlinkSync("./dist/js/build.min.js"); //删除已有的合并文件
    cb();7	
}

复制文件操作:

const fs = require("fs");
	
exports.default = function (cb) {
    let source = "./src/html/",
        target = "./dist/html/";
    //检测如果目录不存在 则创建之
    if (!fs.existsSync(target)) fs.mkdirSync(target, {
        recursive: true, //递归创建
    });

    const files = fs.readdirSync(source);
    files.forEach((item) => {
        let filename = path.join(source, item);
        let buffer = fs.readFileSync(filename);
        fs.writeFileSync(path.join(target, item), buffer);
    });
  
    //可以直接使用src和dest方法直接转化
    //return src(source)
    // .pipe(dest(target));
}

15) gulpif条件插件

因为插件的操作不应该针对特定文件类型,因此你可能需要使用像gulp-if之类的插件来完成转换某些文件的操作。

function chk(file) {
//file为src中遍历出的每个文件对象
//file.history数组中存放了该文件或目录名
//只针对index.js文件的操作
return  file.history[0].endsWith(“index.js”);
}

src(“src/js/*.js”)
.pipe(gulpif(chk, concat()))
.dest()

16) through2内联插件

7.创建任务

每个 gulp 任务(task)都是一个异步的 JavaScript 函数,此函数是一个可以接收 callback 作为参数的函数,或者是一个返回 stream、promise、event emitter、child process 或 observable (后面会详细讲解) 类型值的函数。由于某些平台的限制而不支持异步任务,因此 gulp 还提供了一个漂亮 替代品。
任务(tasks)可以是 public(公开) 或 private(私有) 类型的。

  • 公开任务(Public tasks):它指从 gulpfile 中被导出(export),可以通过 gulp 命令直接调用的任务。
  • 私有任务(Private tasks):它设计被用于内部使用,通常作为 series() 或 parallel() 组合的组成部分。一个私有(private)类型的任务(task)在外观和行为上和其他任务(task)是一样的,但是不能够被用户直接调用。如需将一个任务(task)注册为公开(public)类型的,只需从 gulpfile 中导出(export)即可。

17) 导出任务

使用exports导出任务列表,每个任务即可以是一个字面量函数,也可以是已有函数的引用。可以通过重写module.exports将任务集合在一起。任务函数最好单独声明,这样便于组合式任务中引用它们。

const {series, parallel} = require(“gulp”);

function html()  {}
function css() {}
function js() {}

exports.html = html;
exports.css = css;
exports.js = js;
exports.default = parallel(html, css, js);

//使用module.exports重写
module.exports = {
    html,
    css,
    js43	}

18) 组合任务

除了指定的任务外,gulp命令初始化时会自动加载default任务,通常它是一个组合任务。在组合任务中,所有的任务函数必须独立定制以便接下来在series()和parallel()方法中引用它们。

const {src, dest, series, parallel} = require("gulp");	
const concatCSS = require("gulp-concat-css"),
      rename = require("gulp-rename"),
      lessCSS = require("gulp-less"),
      cleanCSS = require("gulp-clean-css");

//注册多个单一任务,可以使用module.exports
module.exports = {
    concat (cb) {
        console.log(222);
        return src("./src/css/*.css")
            .pipe(concatCSS("./css/build.css"))
            .pipe(rename({extname : ".min.css"}))
            .pipe(cleanCSS({compatibility:"ie8"}))
            .pipe(dest("./dist/"));
    },

    less (cb) {
        console.log(111);
        return src("src/less/*.less")
            .pipe(lessCSS())
            .pipe(dest("./src/css")); //自动加载名称
    },
    default : series(less, concat), //顺序执行任务,保证合并文件中包含less编译文件
    default : parallel(less, concat),//异步执行任务,执行合并任务时less未必完成编译,合并文件中可能会丢失less文件
}

8.异步执行

Node 库以多种方式处理异步功能。最常见的模式是 error-first callbacks,但是你还可能会遇到 streams、promises、event emitters、child processes, 或 observables。gulp 任务(task)规范化了所有这些类型的异步功能。

19) 任务完成通知

当从任务(task)中返回 stream、promise、event emitter、child process 或 observable 时,成功或错误值将通知 gulp 是否继续执行或结束。如果任务(task)出错,gulp 将立即结束执行并显示该错误。
当使用 series() 组合多个任务(task)时,任何一个任务(task)的错误将导致整个任务组合结束,并且不会进一步执行其他任务。当使用 parallel() 组合多个任务(task)时,一个任务的错误将结束整个任务组合的结束,但是其他并行的任务(task)可能会执行完,也可能没有执行完。

20) 返回值类型

  • 返回stream,所有管道运行的任务,最终以输入输出流的形式 返回异步线程

  • 返回Promise,一个Promise对象的实例

  • 返回child process或者observable

  • 使用callback,如果任务(task)不返回任何内容,则必须使用 callback 来指示任务已完成。在如下示例中,callback 将作为唯一一个名为 cb() 的参数传递给你的任务(task)。

    function callbackError(cb) {
        // `cb()` should be called by some async work
        cb(new Error('kaboom'));
    }
    exports.default = callbackError;
    

如需通过 callback 把任务(task)中的错误告知 gulp,请将 Error 作为 callback 的唯一参数。
如果不使用前面提供到几种方式,你还可以将任务(task)定义为一个 async 函数,它将利用 promise 对你的任务(task)进行包装。这将允许你使用 await 处理 promise,并使用其他同步代码。

const fs = require('fs');

async function asyncAwaitTask() {
    const { version } = fs.readFileSync('package.json');
    console.log(version);
    await Promise.resolve('some result');
}

exports.default = asyncAwaitTask;

9.global详解

21) 字符串片段与分隔符

字符串片段(segment)是指两个分隔符之间的所有字符组成的字符串。在 glob 中,分隔符永远是 / 字符 - 不区分操作系统 - 即便是在采用 \ 作为分隔符的 Windows 操作系统中。在 glob 中,\ 字符被保留作为转义符使用。
如下, * 被转义了,因此,* 将被作为一个普通字符使用,而不再是通配符了。

22) 特殊字符: * (一个星号)

在一个字符串片段中匹配任意数量的字符,包括零个匹配。对于匹配单级目录下的文件很有用。
下面这个 glob 能够匹配类似 index.js 的文件,但是不能匹配类似 scripts/index.js 或 scripts/nested/index.js 的文件。

23) 特殊字符: ** (两个星号)

在多个字符串片段中匹配任意数量的字符,包括零个匹配。 对于匹配嵌套目录下的文件很有用。请确保适当地限制带有两个星号的 glob 的使用,以避免匹配大量不必要的目录。
下面这个 glob 被适当地限制在 scripts/ 目录下。它将匹配类似 scripts/index.js、scripts/nested/index.js 和 scripts/nested/twice/index.js 的文件。

24) 特殊字符: ! (取反)

由于 glob 匹配时是按照每个 glob 在数组中的位置依次进行匹配操作的,所以 glob 数组中的取反(negative)glob 必须跟在一个非取反(non-negative)的 glob 后面。第一个 glob 匹配到一组匹配项,然后后面的取反 glob 删除这些匹配项中的一部分。如果取反 glob 只是由普通字符组成的字符串,则执行效率是最高的。

10.监听状态

gulp api 中的 watch() 方法利用文件系统的监控程序(file system watcher)将 globs 与 任务(task) 进行关联。它对匹配 glob 的文件进行监控,如果有文件被修改了就执行关联的任务(task)。如果被执行的任务(task)没有触发 异步完成 信号,它将永远不会再次运行了。
此 API 的默认设置是基于通常的使用场景的,而且提供了内置的延迟和排队机制。

const {
    src,
    dest,
    series,
    parallel,
    watch,
} = require("gulp");

function less() {}
function css() {}
function transform() {}
function js() {}
function html() {}
function img() {}

//监听源文件 一旦发生改变执行全部任务 这样耗时长 没必要 
//watch(“src/*”, parelle(series(less, css), series(transform, js)))

//分别监听源文件的状态,实现热替换,在运行时更新各种模块而无需进行完全刷新
watch("src/css/*", series(less, css))
watch("src/js/*", series(transform, js))
watch("src/image/*", img)
watch("src/*.html" html)

posted @ Zycin (非转载 来自个人学习资料整理)