基本介绍
一个工具包,可以帮你自动化和增加你的工作流
gulp 的核心理念是 task runner
- 可以定义自己的一系列任务,等待任务被执行
- 基于文件 Stream 的构建流
- 我们可以使用 gulp 的插件体系来完成某些任务
webpack 的核心理念是 module bundler
- webpack 是一个模块化的打包工具
- 可以使用各种各样的 loader 来加载不同的模块
- 可以使用各种各样的插件在 webpack 打包的生命周期完成其他的任务
gulp 相对于 webpack 的优缺点:
- gulp 相对于 webpack 思想更加的简单、易用,更适合编写一些自动化的任务
- 但是目前对于大型项目(Vue、React、Angular)并不会使用 gulp 来构建,比如默认 gulp 是不支持模块化的
基本使用
npm install gulp
每个 gulp 任务都是一个异步的 JavaScript 函数
- 此函数可以接受一个 callback 作为参数,调用 callback 函数那么任务会结束
- 或者是一个返回 stream、promise、event emitter、child process 或 observable 类型的函数
任务可以是 public 或者 private 类型的:
- 公开任务(Public tasks) 从 gulpfile 中被导出(export),可以通过 gulp 命令直接调用
- 私有任务(Private tasks) 被设计为在内部使用,通常作为 series() 或 parallel() 组合的组成部分
补充:gulp4 之前, 注册任务时通过 gulp.task 的方式进行注册的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const gulp = require("gulp"); const foo = (cb) => { console.log("第一个gulp任务"); cb(); };
gulp.task("foo2", (cb) => { console.log("第二个gulp任务"); cb(); }); module.exports = { foo, };
|
执行任务 npx gulp foo
默认任务
1 2 3 4
| module.exports.default = (cb) => { console.log("默认任务执行"); cb(); };
|
npx gulp 就会执行默认任务
任务组合 series 和 parallel
series():串行任务组合;
pparallel():并行任务组合;
他们都可以接受任意数量的任务函数或者已经组合的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const foo3 = (cb) => { setTimeout(() => { console.log("定时器任务1"); cb(); }, 2000); };
const foo4 = (cb) => { setTimeout(() => { console.log("定时器任务2"); cb(); }, 1000); };
const seriesTask = series(foo3, foo4); const parallelTask = parallel(foo3, foo4);
module.exports = { seriesTask, parallelTask, };
|
读取和写入文件
1 2
| const copyFile = () => src("./src/*.js").pipe(dest("./dist"));
|
src() 接受参数,并从文件系统中读取文件然后生成一个 Node 流(Stream),它将所有匹配的文件读取到内存中并通过流(Stream)进行处理
- 由 src() 产生的流(stream)应当从任务(task 函数)中返回并发出异步完成的信号
dest() 接受一个输出目录作为参数,并且它还会产生一个 Node 流(stream),通过该流将内容输出到文件中
流(stream)所提供的主要的 API 是 .pipe() 方法,pipe 方法的原理是什么呢?
- pipe 方法接受一个 转换流(Transform streams)或可写流(Writable streams);
- 那么转换流或者可写流,拿到数据之后可以对数据进行处理,再次传递给下一个转换流或者可写流
glob 文件匹配
src() 方法接受一个 glob 字符串或由多个 glob 字符串组成的数组作为参数,用于确定哪些文件需要被操作。
glob 或 glob 数组必须至少匹配到一个匹配项,否则 src() 将报错;
glob 的匹配规则如下:
- (一个星号*):在一个字符串中,匹配任意数量的字符,包括零个匹配
- (两个星号**):在多个字符串匹配中匹配任意数量的字符串,通常用在匹配目录下的文件
(取反!):
- 由于 glob 匹配时是按照每个 glob 在数组中的位置依次进行匹配操作的
- 所以 glob 数组中的取反(negative)glob 必须跟在一个非取反(non-negative)的 glob 后面
- 第一个 glob 匹配到一组匹配项,然后后面的取反 glob 删除这些匹配项中的一部分
1
| ["src/**/*.js", "!src/main"];
|
文件转换
使用插件可以对文件进行修改 比如转 ES5 压缩
npm i 插件
1 2 3 4 5 6 7 8 9 10 11
| const babel = require("gulp-babel"); const terser = require("gulp-terser");
const copyFile = () => src("./src/*.js") .pipe(babel({ presets: ["@babel/preset-env"] })) .pipe(terser({ toplevel: true })) .pipe(dest("./dist"));
|
watch 监听
1 2 3 4 5 6 7 8 9 10
| const copyFile = () => src("./src/*.js") .pipe(babel({ presets: ["@babel/preset-env"] })) .pipe(terser({ mangle: { toplevel: true } })) .pipe(dest("./dist"));
watch("./src/**/*.js", copyFile);
|
这样 当 src 下的 js 文件改变时就会自动触发任务
案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const { src, dest, parallel, series } = require("gulp"); const htmlmin = require("gulp-htmlmin"); const less = require("gulp-less"); const babel = require("gulp-babel"); const terser = require("gulp-terser"); const inject = require("gulp-inject");
const htmlTask = () => src("./src/**/*.html") .pipe(htmlmin({ collapseWhitespace: true })) .pipe(dest("./dist")); const jsTask = () => src("./src/**/*.js") .pipe(babel({ presets: ["@babel/preset-env"] })) .pipe(terser({ mangle: { toplevel: true } })) .pipe(dest("./dist")); const lessTask = () => src("./src/**/*.less").pipe(less()).pipe(dest("./dist"));
const injectTask = () => src("./dist/**/*.html") .pipe( inject(src(["./dist/**/*.js", "./dist/**/*.css"]), { relative: true }) ) .pipe(dest("./dist")); const buildTask = series(parallel(htmlTask, jsTask, lessTask), injectTask); module.exports = { buildTask };
|
本地服务器监听
pnpm add browser-sync
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const browserSync = require("browser-sync");
const bs = browserSync.create(); const serverask = () => { watch("./src/**", buildTask); bs.init({ port: 8081, open: true, files: "./dist", server: { baseDir: "./dist", }, }); };
|
最终版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| const { src, dest, parallel, series, watch } = require("gulp"); const htmlmin = require("gulp-htmlmin"); const less = require("gulp-less"); const babel = require("gulp-babel"); const terser = require("gulp-terser"); const inject = require("gulp-inject"); const browserSync = require("browser-sync");
const htmlTask = () => src("./src/**/*.html") .pipe(htmlmin({ collapseWhitespace: true })) .pipe(dest("./dist"));
const jsTask = () => src("./src/**/*.js") .pipe(babel({ presets: ["@babel/preset-env"] })) .pipe(terser({ mangle: { toplevel: true } })) .pipe(dest("./dist"));
const lessTask = () => src("./src/**/*.less").pipe(less()).pipe(dest("./dist"));
const injectTask = () => src("./dist/**/*.html") .pipe( inject(src(["./dist/**/*.js", "./dist/**/*.css"]), { relative: true }) ) .pipe(dest("./dist")); const build = series(parallel(htmlTask, jsTask, lessTask), injectTask);
const bs = browserSync.create(); const serveTask = () => { watch("./src/**", build); bs.init({ port: 8081, open: true, files: "./dist", server: { baseDir: "./dist", }, }); }; const serve = series(build, serveTask); module.exports = { build, serve };
|
1 2 3 4
| "scripts": { "build": "gulp build", "serve": "gulp serve" },
|
构建 npm run build
服务 npm run serve