gulp

1,535 阅读7分钟

一、简介

Gulp >> 是基于 的自动化构建系统,是前端开发过程中对代码进行构建的工具;gulp不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成;使用gulp,我们不仅可以很愉快的编写代码,而且大大提高我们的工作效率。

gulp是基于 nodeJS 的自动任务运行器, 它能自动化地完成 javascript / sass / less / html / image / css 等文件的的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成等,并监听文件在改动后重复指定的这些步骤。在实现上,gulp借鉴了Unix操作系统的管道(pipe)思想,前一级的输出,直接变成后一级的输入,使得在操作上非常简单。通过本章,我们将学习如何使用gulp来改变开发流程,从而使开发更加快速高效。

Gulp 特点:

  • 任务化:所有的构建操作,在 gulp 中都称之为 任务
  • 基于流:gulp 中所有的文件操作,都是基于 方式进行 ( Gulp有一个自己的内存,通过指定 API 将源文件流到内存中,完成相应的操作后再通过相应的 API 流出去)

二、gulp vs grunt

首先看一篇文章 Gulp的目标是取代Grunt >>

根据 gulp 的文档,它努力实现的主要特性是:

  • 易于使用:采用代码优于配置策略,gulp 让简单的事情继续简单,复杂的任务变得可管理。
  • 高效:通过利用 node.js 强大的流,不需要往磁盘写中间文件,可以更快地完成构建。
  • 高质量:gulp 严格的插件指导方针,确保插件简单并且按你期望的方式工作。
  • 易于学习:通过把 API 降到最少,你能在很短的时间内学会 gulp。构建工作就像你设想的一样:是一系列流管道。

Gulp通过 流和代码优于配置 的策略来简化任务编写的工作。

三、gulp API

使用gulp,仅需知道4个API即可:gulp.task(), gulp.src(), gulp.dest(), gulp.watch(),所以很容易就能掌握。

在开始介绍之前,建议大家先看看 Gulp 中的一些概念 >>

1. gulp.src()

读取数据源并创建一个流,用于从文件系统读取 Vinyl 对象。需要注意的是,这个流里的内容不是原始的文件流,而是一个虚拟文件对象流( Vinyl files ),这个虚拟文件对象中存储着原始文件的路径、文件名、内容等信息,这个我们暂时不用去深入理解,你只需简单的理解可以用这个方法来读取你需要操作的文件就行了。其语法为:

gulp.src(globs, [options])

参数解读

  • globs:参数是文件匹配模式(类似正则表达式),用来匹配文件路径(包括文件名),当然这里也可以直接指定某个具体的文件路径。当有多个匹配模式时,该参数可以为一个数组。

  • options:可选参数,通常情况下我们不需要用到。

gulp 内部使用了 node-glob 模块来实现其文件匹配功能。我们可以使用下面这些特殊的字符来匹配我们想要的文件:

#描述
*匹配文件路径中的0个或多个字符,但不会匹配路径分隔符 /,除非路径分隔符出现在末尾。
**匹配文件路径中的0个或多个目录及其子目录,需要单独出现,即它左右不能有其他东西了。如果出现在末尾,也能匹配文件。
?匹配文件路径中的单个字符(不会匹配路径分隔符)
!取反

想了解更多 glob 匹配规则,请 参考这里 >>

2. gulp.dest()

输出数据流到目标路径,语法形式如下:

gulp.dest(path[, options])

参数解读

  • path:为写入文件的路径
  • options:为一个可选的参数对象,通常我们不需要用到

3. gulp.task()

该方法用来定义任务,其语法为:

gulp.task([taskName], taskFunction)

提示:这个API不再是推荐的模式了,现在主要是直接在文件中直接导出任务函数,请 参考这里 >>

可以通过 seriesparallellastRun api 访问该任务:

# 1. 顺序执行
gulp.series(...tasks)
# 2. 并行执行
gulp.parallel(...tasks)
# 3. 最后执行
gulp.lastRun(task, [precision])

4. gulp.watch()

监听 globs 并在发生更改时运行任务。任务与任务系统的其余部分被统一处理。

watch(globs, [options], [task])

参数解读

  • globs:为要监视的文件匹配模式,规则和用法与 gulp.src() 方法中的 glob 相同。

  • options:为一个可选的配置对象,通常不需要用到。

  • task:任务函数

gulp.watch("./src/js/*.js", gulp.parallel("handleJS"));

四、初体验

1. 基础用法

① 创建项目

$ mkdir hello-gulp && cd hello-gulp && npm init -y

② 安装依赖

$ npm install gulp --save-dev

查看版本:

$ ./node_modules/.bin/gulp --version
CLI version: 2.3.0
Local version: 4.0.2

③ 创建 gulpfile 文件

在根目录创建 gulpfile.js 文件,并创建相应任务:

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

// task a
const a = (done) => {
  setTimeout(() => {
    console.log('Task a is complete!');
    done();
  }, 1000);
};

// task b
const b = (done) => {
  setTimeout(() => {
    console.log('Task b is complete!');
    done();
  }, 1000);
};

// task c
const c = (done) => {
  setTimeout(() => {
    console.log('Task c is complete!');
    done();
  }, 1000);
};

// -- 默认任务
exports.default = series;
// -- 串行方式执行任务,先执行a, 然后执行b, 然后执行c
exports.series = series(a, b, c);
// -- 并行方式执行任务,同时执行a,b,c
exports.parallel = parallel(a, b, c);

提示:series()parallel() 可以被嵌套到任意深度。通过这两个函数,构建任务可以被任意排列组合,从而满足各种复杂的构建需求。

④ 运行任务

接下来,我们执行 gulp 任务:

$ ./node_modules/.bin/gulp # 执行默认任务(default)
$ ./node_modules/.bin/gulp series # 执行series任务
$ ./node_modules/.bin/gulp parallel # 执行parallel任务

2. 文件操作

gulp 暴露了 src()dest() 方法用于处理计算机上存放的文件。在代码构建过程中,需要将源文件,写入到目标目录。

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

exports.default = () => {
  // 将 src/app 目录下的 index.js 文件,复制到 dist/app 目录下
  return src('src/app/index.js', { base: 'src' }).pipe(dest('dist'));
};

五、插件

1. 插件机制

gulp 内部实现用了三个SDK实现:

  • undertaker:主要用来实现gulp的子任务流程管理
  • vinyl-fs.src 接口可以匹配一个文件 通配符,将匹配到的文件转为Vinyl Stream(流),gulp理念就是万物皆可流。
  • glob-watcher:监控文件流变化

核心就是把文件转换成 Stream 流,然后对 Stream 进行操作。

所以 gulp 采用 pipe (管道)的概念,意味着顺着管道流淌,然后我们对于 gulp 的插件,也很好理解了,就是在管道中间有个过滤站,对流进行过滤处理。

2. 常用插件

六、Gulp 实战

安装依赖:

# 通用
$ npm install gulp gulp-rename gulp-load-plugins del browser-sync --save-dev
# 样式
$ npm install gulp-postcss cssnano autoprefixer @fullhuman/postcss-purgecss  gulp-less  --save-dev
# 脚本
$ npm install gulp-babel @babel/core @babel/cli @babel/preset-env gulp-uglify --save-dev 
# 模板
$ npm install gulp-htmlmin --save-dev 
# 图片
$ npm install gulp-imagemin@7.1.0  --save-dev

配置代码:

/*
 * @Author: Lee
 * @Date: 2021-12-26 01:56:07
 * @LastEditors: Lee
 * @LastEditTime: 2021-12-26 18:07:45
 */

// -- 导入模块
const { src, dest, parallel, series, watch } = require('gulp');
const browserSync = require('browser-sync');
const bs = browserSync.create();
const del = require('del');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const purgecss = require('@fullhuman/postcss-purgecss');
const $ = require('gulp-load-plugins')();

// -- 清除文件
const clean = () => {
  return del(['dist']);
};

// -- 服务 & 热更新
const serve = () => {
  // watch(被监视的文件,对应的任务)
  watch('src/index.html', html);
  watch('src/**/*.less', style);
  watch('src/**/*.js', script);
  watch('src/images/**', image);
  // 初始化服务
  bs.init({
    notify: false, // 禁用浏览器右上角的 browserSync connected 提示框
    files: 'dist/**', // 监视 dist 下 文件的变化,然后在浏览器上实时更新
    server: {
      baseDir: './dist', // 指定服务启动的目录
    },
    port: 5000,
  });
};

// -- 处理样式
const style = () => {
  var plugins = [
    autoprefixer({ overrideBrowserslist: ['last 2 version'] }),
    cssnano(),
    purgecss({
      content: ['./src/**/*.html'],
    }),
  ];
  return src('src/styles/*.less', { base: 'src' })
    .pipe($.less())
    .pipe($.postcss(plugins))
    .pipe($.rename({ extname: '.min.css' }))
    .pipe(dest('dist'));
};

// -- 处理脚本
const script = () => {
  return src('src/**/*.js')
    .pipe(
      $.babel({
        presets: ['@babel/preset-env'],
      })
    )
    .pipe($.uglify())
    .pipe($.rename({ extname: '.min.js' }))
    .pipe(dest('dist'));
};

// -- 处理模板
const html = () => {
  return src('src/**/*.html')
    .pipe(
      $.htmlmin({
        collapseWhitespace: true, // 去除标签之间多余的空行和空白
        minifyCSS: true, // 压缩HTML中的CSS代码
        minifyJS: true, // 压缩HTML中的JS代码
      })
    )
    .pipe(dest('dist'));
};

// -- 处理图片
const image = () => {
  return src('src/images/**', { base: 'src' })
    .pipe($.imagemin())
    .pipe(dest('dist'));
};

const build = series(clean, parallel([style, script, html, image]));
const dev = series(build, serve);

module.exports = {
  build,
  dev,
  serve,
};

七、拓展

处理缓存,加 hash 值,请 参考这里 >>