- Published on
Gulp 使用
- Authors
 - Name
- Et cetera
 
 
Table of Contents
Gulp 是什么
Gulp is a toolkit to automate & enhance your workflow
Gulp 是一个基于流的自动化构建工具包,它可以帮助我们自动化地完成一些重复性的工作,比如压缩、编译、合并等等。
Gulp 对比 webpack
Gulp 和 webpack 都是前端构建工具,但是它们的定位不同,webpack 是模块打包工具,而 Gulp 是任务执行工具。
Gulp 的核心理念是 task runner:
- 可以定义自己的一系列任务,等待任务执行 
- 基于文件流的方式,将文件作为输入,经过一系列的处理,输出到指定的位置 
- 可以使用 - gulp的插件体系来完成某些任务
webpack 的核心理念是 module bundler:
- webpack是一个模块化的打包工具
- 可以使用各种各样的 - loader来处理不同类型的模块
- 可以使用各种各样的 - plugin在- webpack不同的打包生命周期来完成各种各样的任务
Gulp 相对于 webpack 的优缺点:
- Gulp相对于- webpack更加简单、灵活、易用,更适合编写一些自动化的任务
- 但是 - Gulp本身并不支持模块化,需要借助- browserify或者- webpack来完成模块化的打包
Gulp 的使用
安装
pnpm add gulp -D
touch gulpfile.js
第一个任务
另外以下都是同步任务,Gulp 是支持将任务定义为异步任务的,而异步任务的完成需要调用 cb 回调函数或者返回一个 Promise、stream、event emitter、chile process、observable。
// gulpfile.js
const foo = (cb) => {
  console.log('first gulp task')
  cb()
}
// 需要导出任务才能被 gulp 执行
module.exports = { foo }
然后命令行执行 pnpx gulp foo,就可以看到输出了。
[16:59:21] Using gulpfile ~/Web_Project/demo/gulpfile.js
[16:59:21] Starting 'foo'...
first gulp task
[16:59:21] Finished 'foo' after 716 μs
另外补充之前旧版本 Gulp 中编写任务的方式:
const gulp = require('gulp')
gulp.task('foo', (cb) => {
  console.log('first gulp task')
  cb()
})
module.exports = { foo }
Gulp 的转义
- 使用 - babel来转义- gulpfile.js:- pnpm add @babel/register -D # rename gulpfile.js to gulpfile.babel.js
- 使用 - ts-node来转义- gulpfile.js:- pnpm add ts-node -D # rename gulpfile.js to gulpfile.ts
- 使用 - esm来转义- gulpfile.js:- pnpm add esm -D # rename gulpfile.js to gulpfile.esm.js- // gulpfile.esm.js // 然后就支持 es module 语法了 const foo = (cb) => { console.log('first gulp task') cb() } // 需要导出任务才能被 gulp 执行 export default foo- 运行 - pnpx gulp foo,可以看到输出了:- [17:23:21] Requiring external module esm [17:23:21] Using gulpfile ~/Web_Project/demo/gulpfile.esm.js [17:23:21] Starting 'foo'... first gulp task [17:23:21] Finished 'foo' after 739 μs
Gulp task
从 Gulp 4.0 开始,Gulp 将任务分为 public tasks 和 private tasks,public tasks 可以被命令行直接执行,而 private tasks 只能被其他任务调用。
public tasks
从 gulpfile 中导出的任务都是 public tasks,可以通过 pnpx gulp --tasks 来查看所有的 public tasks:
[17:23:21] Requiring external module esm
[17:23:21] Tasks for ~/Web_Project/demo/gulpfile.esm.js
[17:23:21] ├── foo
[17:23:21] └─┬ default
[17:23:21]   └─┬ <series>
[17:23:21]     └── foo
private tasks
private tasks 被设计为在内部使用,通常作为 series() 或 parallel() 的参数,不会被命令行直接执行,也不会被其他任务直接调用。
// gulpfile.esm.js
import { series } from 'gulp'
// 这里 foo 是 private task,只会被 series 调用
export const foo = (cb) => {
  console.log('first gulp task')
  cb()
}
const bar = (cb) => {
  console.log('second gulp task')
  cb()
}
// export default series(foo, bar)
// 默认任务, 执行 `pnpx gulp` 就会执行
export default (cb) => {
  console.log('default gulp task')
  cb()
}
运行 pnpx gulp --tasks,可以看到输出了:
[17:29:45] Requiring external module esm
[17:29:46] Tasks for ~/Web_Project/demo/gulpfile.esm.js
[17:29:46] ├── foo
[17:29:46] └─┬ default
[17:29:46]   └─┬ <series>
[17:29:46]     ├── foo
[17:29:46]     └── bar
compose tasks
Gulp 提供了 series() 和 parallel() 来组合任务,series() 会按照顺序执行任务,而 parallel() 会并行执行任务。
To have your tasks execute in order, use the series() method.
series() 使用示例:
// gulpfile.esm.js
import { series } from 'gulp'
const foo = (cb) => {
  setTimeout(() => {
    console.log('first gulp task')
    cb()
  }, 1000)
}
const bar = (cb) => {
  setTimeout(() => {
    console.log('second gulp task')
    cb()
  }, 2000)
}
export default series(foo, bar)
运行 pnpx gulp,可以看到输出了:
[17:59:32] Requiring external module esm
[17:59:33] Using gulpfile ~/Web_Project/demo/gulpfile.esm.js
[17:59:33] Starting 'default'...
[17:59:33] Starting 'foo'...
first gulp task
[17:59:34] Finished 'foo' after 1.01 s
[17:59:34] Starting 'bar'...
second gulp task
[17:59:36] Finished 'bar' after 2 s
[17:59:36] Finished 'default' after 3.01 s
For tasks to run at maximum concurrency, combine them with the parallel() method.
parallel() 使用示例:
// gulpfile.esm.js
import { parallel } from 'gulp'
const foo = (cb) => {
  setTimeout(() => {
    console.log('first gulp task')
    cb()
  }, 1000)
}
const bar = (cb) => {
  setTimeout(() => {
    console.log('second gulp task')
    cb()
  }, 2000)
}
export default parallel(foo, bar)
运行 pnpx gulp,可以看到输出了:
[18:00:51] Requiring external module esm
[18:00:51] Using gulpfile ~/Web_Project/demo/gulpfile.esm.js
[18:00:51] Starting 'default'...
[18:00:51] Starting 'foo'...
[18:00:51] Starting 'bar'...
first gulp task
[18:00:52] Finished 'foo' after 1 s
second gulp task
[18:00:53] Finished 'bar' after 2 s
[18:00:53] Finished 'default' after 2 s
对比任务执行结束时间可以看到 series() 是按照顺序执行任务,用了 3s; 而 parallel() 是并行执行任务,用了 2s。
嵌套任务
series()andparallel()can be nested to any arbitrary depth.
// gulpfile.js
const { series, parallel } = require('gulp')
function clean(cb) {
  // body omitted
  cb()
}
function cssTranspile(cb) {
  // body omitted
  cb()
}
function cssMinify(cb) {
  // body omitted
  cb()
}
function jsTranspile(cb) {
  // body omitted
  cb()
}
function jsBundle(cb) {
  // body omitted
  cb()
}
function jsMinify(cb) {
  // body omitted
  cb()
}
function publish(cb) {
  // body omitted
  cb()
}
exports.build = series(
  clean,
  parallel(cssTranspile, series(jsTranspile, jsBundle)),
  parallel(cssMinify, jsMinify),
  publish
)
Gulp 插件
Gulp 读写文件
Gulp 提供了 src() 和 dest() 来读写文件。
// gulpfile.esm.js
import { src, dest } from 'gulp'
// src() 和 dest() 返回值都是 ReadWriteStream
const copyFile = () => src('./oor.txt').pipe(dest('dist'))
export default copyFile
Gulp-babel
Gulp 本身不支持 babel,需要借助 gulp-babel 插件来完成 babel 的转义。
pnpm add gulp-babel @babel/core @babel/preset-env -D
// src/main.js
const singer = 'aimyon'
const foo = () => {
  console.log('foo')
}
foo()
通过 .babelrc 配置文件来配置 babel:
{
  "presets": ["@babel/preset-env"]
}
// gulpfile.esm.js
import { src, dest } from 'gulp'
import babel from 'gulp-babel'
const parseJS = () =>
  src('src/*.js')
    .pipe(
      babel()
      // 也可以不配置 .babelrc,直接在这里配置
      // babel('@babel/preset-env')
    )
    .pipe(dest('dist'))
export default parseJS
同样执行一下 pnpx gulp,可以看到输出了:
[21:30:49] Requiring external module esm
[21:30:50] Using gulpfile ~/Web_Project/demo/gulpfile.esm.js
[21:30:50] Starting 'default'...
[21:30:51] Finished 'default' after 752 ms
然后 dist 文件夹下确实也有转义后的文件了。
// dist/main.js
'use strict'
var singer = 'aimyon'
var foo = function foo() {
  console.log('foo')
}
foo()
Gulp-terser
Gulp 本身不支持 terser,需要借助 gulp-terser 插件来完成 terser 的压缩。
pnpm add gulp-terser -D
// gulpfile.esm.js
import { src, dest } from 'gulp'
import babel from 'gulp-babel'
import terser from 'gulp-terser'
const parseJS = () =>
  src('src/*.js')
    // 转义
    .pipe(babel())
    // 压缩
    .pipe(terser())
    .pipe(dest('dist'))
export default parseJS
// dist/main.js
// 可以看到其实丑化还不完全
"use strict";var singer="aimyon",foo=function(){console.log("foo")};foo();
再修改 gulpfile :
import { src, dest } from 'gulp'
import babel from 'gulp-babel'
import terser from 'gulp-terser'
const parseJS = () =>
  src('src/*.js')
    // 转义
    .pipe(babel())
    // 压缩
    .pipe(terser({ mangle: { toplevel: true } }))
    .pipe(dest('dist'))
export default parseJS
"use strict";var o="aimyon",n=function(){console.log("foo")};n();
再进行更进一步配置:
// gulpfile.esm.js
import { src, dest } from 'gulp'
import babel from 'gulp-babel'
import terser from 'gulp-terser'
const parseJS = () =>
  src('src/*.js')
    // 转义
    .pipe(babel())
    // 压缩
    // .pipe(terser({ mangle: { toplevel: true } }))
    .pipe(terser({ toplevel: true }))
    .pipe(dest('dist'))
export default parseJS
"use strict";console.log("foo");
watch
Gulp 提供了 watch() 来监听文件变化,然后执行任务。
import { src, dest, watch } from 'gulp'
import babel from 'gulp-babel'
import terser from 'gulp-terser'
const parseJS = () =>
  src('src/*.js')
    // 转义
    .pipe(babel())
    // 压缩
    .pipe(terser({ mangle: { toplevel: true } }))
    // .pipe(terser({ toplevel: true }))
    .pipe(dest('dist'))
// 监听文件变化
watch('src/**/*.js', parseJS)
export default parseJS
使用 Gulp 构建工程
pnpm add gulp gulp-babel gulp-terser gulp-less gulp-htmlmin gulp-inject browser-sync -D
// gulpfile.esm.js
import { src, dest, watch, series, parallel } from 'gulp'
import babel from 'gulp-babel'
import terser from 'gulp-terser'
import htmlmin from 'gulp-htmlmin'
import less from 'gulp-less'
import inject from 'gulp-inject'
import browserSync from 'browser-sync'
// 1.对html进行打包
const htmlTask = () =>
  src('src/**/*.html')
    .pipe(htmlmin({ collapseWhitespace: true }))
    .pipe(dest('dist'))
const parseJS = () =>
  src('src/*.js')
    // 转义
    .pipe(babel())
    // 压缩
    .pipe(terser({ mangle: { toplevel: true } }))
    // .pipe(terser({ toplevel: true }))
    .pipe(dest('dist'))
// 3.对less进行打包
const lessTask = () => src('src/**/*.less').pipe(less()).pipe(dest('dist'))
// 4.在html中注入js和css
const injectTask = () =>
  src('dist/**/*.html')
    .pipe(inject(src(['dist/**/*.js', 'dist/**/*.css']), { relative: true }))
    .pipe(dest('dist'))
// 5.开启一个本地服务器
const bs = browserSync.create()
const serve = () => {
  watch('src/**', buildTask)
  bs.init({
    port: 8080,
    open: true,
    files: 'dist/*',
    server: {
      baseDir: 'dist',
    },
  })
}
// 创建项目构建的任务
const buildTask = series(parallel(htmlTask, parseJS, lessTask), injectTask)
const serveTask = series(buildTask, serve)
// webpack搭建本地 webpack-dev-server
export default series(buildTask, serveTask)
然后建立根目录下建立 src 文件夹,然后在 src 文件夹下建立 index.html、index.js、index.less。
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Gulp Projecct</title>
    <!-- inject:css -->
    <!-- endinject -->
  </head>
  <body>
    <!-- inject:js -->
    <!-- endinject -->
  </body>
</html>
// src/less/style.less
@mainColor: skyblue;
body {
  background-color: @mainColor;
}
// src/index.js
const singer = 'aimyon'
const foo = () => {
  console.log('foo')
}
foo()
最后执行 pnpx gulp,就可以看到效果了。
[22:24:42] Requiring external module esm
[22:24:43] Using gulpfile ~/Web_Project/demo/gulpfile.esm.js
[22:24:43] Starting 'default'...
[22:24:43] Starting 'htmlTask'...
[22:24:43] Starting 'parseJS'...
[22:24:43] Starting 'lessTask'...
[22:24:43] Finished 'parseJS' after 10 ms
[22:24:43] Finished 'htmlTask' after 23 ms
[22:24:43] Finished 'lessTask' after 24 ms
[22:24:43] Starting 'injectTask'...
[22:24:43] gulp-inject 2 files into index.html.
[22:24:43] Finished 'injectTask' after 6.85 ms
[22:24:43] Starting 'htmlTask'...
[22:24:43] Starting 'parseJS'...
[22:24:43] Starting 'lessTask'...
[22:24:44] Finished 'parseJS' after 3.23 ms
[22:24:44] Finished 'lessTask' after 8.31 ms
[22:24:44] Finished 'htmlTask' after 8.84 ms
[22:24:44] Starting 'injectTask'...
[22:24:44] gulp-inject 2 files into index.html.
[22:24:44] Finished 'injectTask' after 7.63 ms
[22:24:44] Starting 'serve'...
[Browsersync] Access URLs:
 ---------------------------------------
       Local: http://localhost:8080
    External: http://192.168.18.161:8080
 ---------------------------------------
          UI: http://localhost:3001
 UI External: http://localhost:3001
 ---------------------------------------
[Browsersync] Serving files from: dist
[Browsersync] Watching files...
使用 Gulp 打包 es 、 commonjs 、 umd 模块
pnpm add gulp @babel/core @babel/preset-env gulp-babel gulp-terser gulp-typescript typescript del @babel/plugin-transform-modules-commonjs -D
# 然后设置 package.json 中 type: module
// 因为现在可以通过 type: module 来使用 es module 语法,所以可以直接使用 es module 语法
// gulpfile.mjs
import gulp from 'gulp'
import babel from 'gulp-babel'
import ts from 'gulp-typescript'
import { deleteAsync as del } from 'del'
const { src, dest, series } = gulp
// 每次构建前先清空之前的构建结果
const clean = async () => {
  await del('es/**')
  await del('lib/**')
  await del('cjs/**')
}
// 从 ts 生成 es 模块
const genES = () => {
  const tsProject = ts.createProject('tsconfig.json', {
    module: 'ESNext',
  })
  return tsProject.src().pipe(tsProject()).pipe(babel()).pipe(dest('src/es/'))
}
// 从 ts 生成 commonjs 模块
const genCJS = () => {
  return src('src/es/**/*.js')
    .pipe(
      babel({
        presets: [['@babel/env']],
        plugins: ['@babel/plugin-transform-modules-commonjs'],
      })
    )
    .pipe(dest('src/lib/'))
}
// 构建声明文件并输出到 es 和 lib 目录下
const genDeclaration = () => {
  const tsProject = ts.createProject('tsconfig.json', {
    declaration: true,
    emitDeclarationOnly: true,
  })
  return tsProject.src().pipe(tsProject()).pipe(dest('src/es/')).pipe(dest('src/lib/'))
}
export default series(clean, genES, genCJS, genDeclaration)