- 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)