1.什么是gulp #

2. gulp特点 #

3. 安装gulp #

npm install --g gulp-cli
npm install --save-dev gulp

4. 异步任务和组合任务 #

gulpfile.js

const fs = require('fs');
const through = require('through2');
const { series, parallel } = require('gulp');
function callbackTask(done) {
    setTimeout(() => {
        console.log('callbackTask');
        done();
    }, 1000);
}
function promiseTask() {
    return new Promise((resolve) => {
        setTimeout(() => {
            console.log('promiseTask');
            resolve();
        }, 1000);
    });
}
async function asyncTask() {
    await new Promise((resolve) => {
        setTimeout(() => {
            resolve();
        }, 1000);
    });
    console.log('asyncTask');
}
function streamTask() {
    return fs.createReadStream('input.txt')
        .pipe(through((chunk, encoding, next) => {
            setTimeout(() => {
                next(null, chunk);
            }, 1000);
        }))
        .pipe(fs.createWriteStream('output.txt'))
}

const parallelTask = parallel(callbackTask, promiseTask, asyncTask, streamTask);
const seriesTask = series(callbackTask, promiseTask, asyncTask, streamTask);
exports.callback = callbackTask
exports.promise = promiseTask
exports.async = asyncTask
exports.stream = streamTask
exports.parallel = parallelTask
exports.series = seriesTask

5. gulp核心API #

5.1 gulp.src() #

gulp.src(globs[, options])

5.2 gulp.dest() #

gulp.dest(path[,options])

5.3 pipe #

gulpfile.js

const { src, dest } = require('gulp');
function copyTask() {
    console.log('执行拷贝任务');
    return src('src/**/*.js').pipe(dest('dist'));
}
exports.default = copyTask;

6. gulp实战 #

6.1 安装 #

cnpm install @babel/core @babel/preset-env browser-sync gulp gulp-babel gulp-clean gulp-clean-css gulp-ejs gulp-htmlmin gulp-if gulp-imagemin gulp-less gulp-load-plugins gulp-uglify gulp-useref gulp-watch map-stream bootstrap jquery --save

6.2 编译样式 #

const { src, dest } = require('gulp');
const less = require('gulp-less');
const styles = () => {
    return src("src/styles/*.less", { base: 'src' })
        .pipe(less())
        .pipe(dest('dist'))
}

exports.styles = styles;

6.3 编译脚本 #

const { src, dest } = require('gulp');
const less = require('gulp-less');
const babel = require('gulp-babel');
const styles = () => {
    return src("src/styles/*.less", { base: 'src' })
        .pipe(less())
        .pipe(dest('dist'))
}

+const scripts = () => {
+    return src("src/scripts/*.js", { base: 'src' })
+        .pipe(babel({
+            presets: ["@babel/preset-env"]
+        }))
+        .pipe(dest('dist'))
+}

exports.styles = styles;
+exports.scripts = scripts;

6.4 编译html #

6.4.1 src\index.html #

<!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><%=title%></title>
        <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css"/>
    <link rel="stylesheet" href="styles/main.css"/>
</head>
<body>
    <button class="btn btn-danger">按钮</button>
    <img src="assets/images/circle.svg"/>
    <img src="rect.svg"/>
    <script src="node_modules/jquery/dist/jquery.js"></script>
    <script src="scripts/main.js"></script>
</body>
</html>

6.4.2 circle.svg #

src\assets\images\circle.svg

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100" height="100" version="1.1" xmlns="http://www.w3.org/2000/svg">
 <circle cx="50" cy="50" r="50"  fill="red"/>
</svg>

6.4.3 static\rect.svg #

static\rect.svg

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100" height="100" version="1.1" xmlns="http://www.w3.org/2000/svg">
 <rect  width="100" height="100" style="fill:red;"/>
</svg>

6.4.4 gulpfile.js #

const { src, dest, parallel } = require('gulp');
const less = require('gulp-less');
const babel = require('gulp-babel');
const ejs = require('gulp-ejs');
const styles = () => {
    return src("src/styles/*.less", { base: 'src' })
        .pipe(less())
        .pipe(dest('dist'))
}

const scripts = () => {
    return src("src/scripts/*.js", { base: 'src' })
        .pipe(babel({
            presets: ["@babel/preset-env"]
        }))
        .pipe(dest('dist'))
}

+const html = () => {
+    return src("src/*.html", { base: 'src' })
+        .pipe(ejs({ title: 'gulp' }))
+        .pipe(dest('dist'))
+}
exports.styles = styles;
exports.scripts = scripts;
+exports.html = html;

6.5 编译任务 #

const { src, dest, parallel } = require('gulp');
const less = require('gulp-less');
const babel = require('gulp-babel');
const ejs = require('gulp-ejs');
const styles = () => {
    return src("src/styles/*.less", { base: 'src' })
        .pipe(less())
        .pipe(dest('dist'))
}

const scripts = () => {
    return src("src/scripts/*.js", { base: 'src' })
        .pipe(babel({
            presets: ["@babel/preset-env"]
        }))
        .pipe(dest('dist'))
}

const html = () => {
    return src("src/*.html", { base: 'src' })
        .pipe(ejs({ title: 'gulp' }))
        .pipe(dest('dist'))
}
+const compile = parallel(styles, scripts, html);
exports.styles = styles;
exports.scripts = scripts;
exports.html = html;
+exports.compile = compile;

6.6 压缩图片 #

6.6.1 安装 #

npm install gulp-imagemin --save-dev
npm install imagemin-jpegtran imagemin-svgo imagemin-gifsicle imagemin-optipng --save-dev

6.6.2 gulpfile.js #

const { src, dest, parallel } = require('gulp');
const less = require('gulp-less');
const babel = require('gulp-babel');
const ejs = require('gulp-ejs');
const styles = () => {
    return src("src/styles/*.less", { base: 'src' })
        .pipe(less())
        .pipe(dest('dist'))
}

const scripts = () => {
    return src("src/scripts/*.js", { base: 'src' })
        .pipe(babel({
            presets: ["@babel/preset-env"]
        }))
        .pipe(dest('dist'))
}

const html = () => {
    return src("src/*.html", { base: 'src' })
        .pipe(ejs({ title: 'gulp' }))
        .pipe(dest('dist'))
}

+const images = async () => {
+    let imagemin = await import('gulp-imagemin');
+    return src("src/assets/images/**/*.@(jpg|png|gif|svg)", { base: 'src' })
+        .pipe(imagemin.default())
+        .pipe(dest('dist'))
+}

const compile = parallel(styles, scripts, html);
exports.styles = styles;
exports.scripts = scripts;
exports.html = html;
exports.compile = compile;
+exports.images = images;

6.7 拷贝静态文件 #

const { src, dest, parallel } = require('gulp');
const less = require('gulp-less');
const babel = require('gulp-babel');
const ejs = require('gulp-ejs');
const styles = () => {
    return src("src/styles/*.less", { base: 'src' })
        .pipe(less())
        .pipe(dest('dist'))
}

const scripts = () => {
    return src("src/scripts/*.js", { base: 'src' })
        .pipe(babel({
            presets: ["@babel/preset-env"]
        }))
        .pipe(dest('dist'))
}

const html = () => {
    return src("src/*.html", { base: 'src' })
        .pipe(ejs({ title: 'gulp' }))
        .pipe(dest('dist'))
}

const images = async () => {
    let imagemin = await import('gulp-imagemin');
    return src("src/assets/images/**/*.@(jpg|png|gif|svg)", { base: 'src' })
        .pipe(imagemin.default())
        .pipe(dest('dist'))
}

+const static = async () => {
+    return src("static/**", { base: 'static' })
+        .pipe(dest('dist'))
+}

const compile = parallel(styles, scripts, html);
+const build = parallel(compile, static)
exports.styles = styles;
exports.scripts = scripts;
exports.html = html;
exports.compile = compile;
exports.images = images;
+exports.static = static;
+exports.build = build;

6.8 删除输出目录 #

const { src, dest, parallel, series } = require('gulp');
const less = require('gulp-less');
const babel = require('gulp-babel');
const ejs = require('gulp-ejs');
+const gulpClean = require('gulp-clean');
const styles = () => {
    return src("src/styles/*.less", { base: 'src' })
        .pipe(less())
        .pipe(dest('dist'))
}

const scripts = () => {
    return src("src/scripts/*.js", { base: 'src' })
        .pipe(babel({
            presets: ["@babel/preset-env"]
        }))
        .pipe(dest('dist'))
}

const html = () => {
    return src("src/*.html", { base: 'src' })
        .pipe(ejs({ title: 'gulp' }))
        .pipe(dest('dist'))
}

const images = async () => {
    let imagemin = await import('gulp-imagemin');
    return src("src/assets/images/**/*.@(jpg|png|gif|svg)", { base: 'src' })
        .pipe(imagemin.default())
        .pipe(dest('dist'))
}

const static = async () => {
    return src("static/**", { base: 'static' })
        .pipe(dest('dist'))
}
+const clean =  () => {
+    return src("dist/**", { read: false })
+        .pipe(gulpClean())
+}
const compile = parallel(styles, scripts, html);
+const build = series(clean, parallel(compile, static));
exports.styles = styles;
exports.scripts = scripts;
exports.html = html;
exports.compile = compile;
exports.images = images;
exports.static = static;
exports.build = build;
+exports.clean = clean;

6.9 自动加载插件 #

const { src, dest, parallel, series } = require('gulp');
-const less = require('gulp-less');
-const babel = require('gulp-babel');
-const ejs = require('gulp-ejs');
-const gulpClean = require('gulp-clean');
+const plugins = require('gulp-load-plugins')();
const styles = () => {
    return src("src/styles/*.less", { base: 'src' })
+       .pipe(plugins.less())
        .pipe(dest('dist'))
}

const scripts = () => {
    return src("src/scripts/*.js", { base: 'src' })
+       .pipe(plugins.babel({
            presets: ["@babel/preset-env"]
        }))
        .pipe(dest('dist'))
}

const html = () => {
    return src("src/*.html", { base: 'src' })
+       .pipe(plugins.ejs({ title: 'gulp' }))
        .pipe(dest('dist'))
}

const images = async () => {
     let imagemin = await import('gulp-imagemin');
    return src("src/assets/images/**/*.@(jpg|png|gif|svg)", { base: 'src' })
        .pipe(imagemin.default())
        .pipe(dest('dist'))
}

const static = async () => {
    return src("static/**", { base: 'static' })
        .pipe(dest('dist'))
}
const clean =  () => {
    return src("dist/**", { read: false })
+       .pipe(plugins.clean())
}
const compile = parallel(styles, scripts, html);
const build = series(clean, parallel(compile, static));
exports.styles = styles;
exports.scripts = scripts;
exports.html = html;
exports.compile = compile;
exports.images = images;
exports.static = static;
exports.build = build;
exports.clean = clean;

6.10 开发服务器 #

6.10.1 gulpfile.js #

const { src, dest, parallel, series } = require('gulp');
const plugins = require('gulp-load-plugins')();
+const browserSync = require('browser-sync');
+const path = require('path');
const styles = () => {
    return src("src/styles/*.less", { base: 'src' })
        .pipe(plugins.less())
        .pipe(dest('dist'))
}

const scripts = () => {
    return src("src/scripts/*.js", { base: 'src' })
        .pipe(plugins.babel({
            presets: ["@babel/preset-env"]
        }))
        .pipe(dest('dist'))
}

const html = () => {
    return src("src/*.html", { base: 'src' })
        .pipe(plugins.ejs({ title: 'gulp' }))
        .pipe(dest('dist'))
}

const images = async () => {
    let imagemin = await import('gulp-imagemin');
    return src("src/assets/images/**/*.@(jpg|png|gif|svg)", { base: 'src' })
        .pipe(imagemin.default())
        .pipe(dest('dist'))
}

const static = async () => {
    return src("static/**", { base: 'static' })
        .pipe(dest('dist'))
}
const clean = () => {
    return src("dist/**", { read: false })
        .pipe(plugins.clean())
}
+const serve = () => {
+    return browserSync.create().init({
+        notify: false,
+        server: {
+            baseDir: 'dist',
+            routes: {
+                '/node_modules': path.resolve('node_modules')
+            }
+        }
+    });
+}
const compile = parallel(styles, scripts, html);
const build = series(clean, parallel(compile, static));
exports.styles = styles;
exports.scripts = scripts;
exports.html = html;
exports.compile = compile;
exports.images = images;
exports.static = static;
exports.build = build;
exports.clean = clean;
+exports.serve = serve;

6.10.2 main.js #

src\scripts\main.js

let sum = (a, b) => a + b;
sum(1, 3)
+$(() => {
+    console.log('jquery');
+});

6.11 监听文件变化 #

6.11.1 gulpfile.js #

const { src, dest, parallel, series, watch } = require('gulp');
const plugins = require('gulp-load-plugins')();
const browserSync = require('browser-sync');
const path = require('path');
+const browserServer = browserSync.create();
const styles = () => {
    return src("src/styles/*.less", { base: 'src' })
        .pipe(plugins.less())
        .pipe(dest('dist'))
}

const scripts = () => {
    return src("src/scripts/*.js", { base: 'src' })
        .pipe(plugins.babel({
            presets: ["@babel/preset-env"]
        }))
        .pipe(dest('dist'))
}

const html = () => {
    return src("src/*.html", { base: 'src' })
+       .pipe(plugins.ejs({ title: 'gulp' }, { cache: false }))
        .pipe(dest('dist'))
}

const images = async () => {
    let imagemin = await import('gulp-imagemin');
    return src("src/assets/images/**/*.@(jpg|png|gif|svg)", { base: 'src' })
        .pipe(imagemin.default())
        .pipe(dest('dist'))
}

const static = async () => {
    return src("static/**", { base: 'static' })
        .pipe(dest('dist'))
}
const clean = () => {
    return src("dist/**", { read: false })
        .pipe(plugins.clean())
}
const serve = () => {
+   watch("src/styles/*.less", styles);
+   watch("src/scripts/*.js", scripts);
+   watch("src/*.html", html);
+   watch([
+       "src/assets/images/**/*.@(jpg|png|gif|svg)",
+       "static/**"
+   ], browserServer.reload);
    return browserServer.init({
        notify: false,
+       files: ['dist/**'],
        server: {
+           baseDir: ['dist', 'src', 'static'],
            routes: {
                '/node_modules': path.resolve('node_modules')
            }
        }
    });
}
const compile = parallel(styles, scripts, html);
+const build = series(clean, parallel(compile, images, static));
+const dev = series(clean, compile, serve);
exports.styles = styles;
exports.scripts = scripts;
exports.html = html;
exports.compile = compile;
exports.images = images;
exports.static = static;
exports.clean = clean;
exports.serve = serve;
+exports.build = build;
+exports.dev = dev;

6.12. 合并和压缩 #

6.12.1 src\index.html #

src\index.html

<!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><%=title%></title>
+   <!-- build:css styles/dist.css  -->
    <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.css"/>
    <link rel="stylesheet" href="styles/main.css"/>
+   <!-- endbuild -->
</head>
<body>
    <button class="btn btn-danger">按钮</button>
    <img src="assets/images/circle.svg"/>
    <img src="rect.svg"/>
+   <!-- build:js scripts/dist.js  -->
    <script src="node_modules/jquery/dist/jquery.js"></script>
    <script src="scripts/main.js"></script>
+   <!-- endbuild -->
</body>
</html>

6.12.2 gulpfile.js #

const { src, dest, parallel, series, watch } = require('gulp');
const plugins = require('gulp-load-plugins')();
const browserSync = require('browser-sync');
const path = require('path');
const browserServer = browserSync.create();
const styles = () => {
    return src("src/styles/*.less", { base: 'src' })
        .pipe(plugins.less())
+       .pipe(dest('temp'))
}

const scripts = () => {
    return src("src/scripts/*.js", { base: 'src' })
        .pipe(plugins.babel({
            presets: ["@babel/preset-env"]
        }))
+       .pipe(dest('temp'))
}

const html = () => {
    return src("src/*.html", { base: 'src' })
        .pipe(plugins.ejs({ title: 'gulp' }, { cache: false }))
+       .pipe(dest('temp'))
}

const images = async () => {
    let imagemin = await import('gulp-imagemin');
    return src("src/assets/images/**/*.@(jpg|png|gif|svg)", { base: 'src' })
        .pipe(imagemin.default())
        .pipe(dest('dist'))
}

const static = async () => {
    return src("static/**", { base: 'static' })
        .pipe(dest('dist'))
}
const clean = () => {
+   return src(["dist/**", "temp/**"], { read: false })
+       .pipe(plugins.clean({ allowEmpty: true }));
}
const serve = () => {
    watch("src/styles/*.less", styles);
    watch("src/scripts/*.js", scripts);
    watch("src/*.html", html);
    watch([
        "src/assets/images/**/*.@(jpg|png|gif|svg)",
        "static/**"
    ], browserServer.reload);
    return browserServer.init({
        notify: false,
        files: ['dist/**'],
        server: {
+           baseDir: ['temp', 'src', 'static'],
            routes: {
                '/node_modules': path.resolve('node_modules')
            }
        }
    });
}

+const concat = () => {
+    return src('temp/*.html', { base: 'temp' })
+        .pipe(plugins.useref({
+            searchPath: ['temp', '.']
+        }))
+        .pipe(plugins.if('*.html', plugins.htmlmin({
+            collapseWhitespace: true,
+            minifyCSS: true,
+            minifyJS: true
+        })))
+        .pipe(plugins.if('*.js', plugins.uglify()))
+        .pipe(plugins.if('*.css', plugins.cleanCss()))
+        .pipe(dest('dist'));
+}

const compile = parallel(styles, scripts, html);
+const build = series(clean, parallel(series(compile, concat), images, static));
const dev = series(clean, compile, serve);
-exports.styles = styles;
-exports.scripts = scripts;
-exports.html = html;
-exports.compile = compile;
-exports.images = images;
-exports.static = static;
-exports.serve = serve;
-exports.concat = concat;
-exports.clean = clean;
-exports.build = build;
-exports.dev = dev;

+module.exports = {
+    clean,
+    build,
+    dev
+}

7. 参考知识 #

7.1 glob #

7.1.1 glob规则 #

匹配符 说明
* 匹配文件路径中的0个或多个字符,但不会匹配路径分隔符
** 匹配路径中的0个或多个目录及其子目录
? 匹配文件路径中的一个字符(不会匹配路径分隔符)
[...] 匹配方括号中出现的字符中的任意一个,当方括号中第一个字符为^或!时,则表示不匹配方括号中出现的其他字符中的任意一个
!(pattern1 pattern2 pattern3) 匹配任何与括号中给定的任一模式都不匹配的
?(pattern1 pattern2 pattern3) 匹配括号中给定的任一模式0次或1次,类似于js正则中的(pattern1 pattern2 pattern3)?
+(pattern1 patter2n pattern3) 匹配括号中给定的任一模式至少1次,类似于js正则中的(pattern1 pattern2 pattern3)+
(pattern1 pattern2 pattern3) 匹配括号中给定的任一模式0次或多次,类似于js正则中的(pattern1 pattern2 pattern3)*
@(pattern1 pattern2 pattern3) 匹配括号中给定的任一模式1次,类似于js正则中的(pattern1 pattern2 pattern3)

7.1.2 glob示例 #

glob 匹配
* 能匹配 a.js,x.y,abc,abc/,但不能匹配a/b.js
. a.js,style.css,a.b,x.y
//*.js 能匹配 a/b/c.js,x/y/z.js,不能匹配a/b.js,a/b/c/d.js
** 能匹配 abc,a/b.js,a/b/c.js,x/y/z,x/y/z/a.b,能用来匹配所有的目录和文件
a/**/z 能匹配 a/z,a/b/z,a/b/c/z,a/d/g/h/j/k/z
a/**b/z 能匹配 a/b/z,a/sb/z,但不能匹配a/x/sb/z,因为只有单**单独出现才能匹配多级目录
?.js 能匹配 a.js,b.js,c.js
a?? 能匹配 a.b,abc,但不能匹配ab/,因为它不会匹配路径分隔符
[xyz].js 只能匹配 x.js,y.js,z.js,不会匹配xy.js,xyz.js等,整个中括号只代表一个字符
[^xyz].js 能匹配 a.js,b.js,c.js等,不能匹配x.js,y.js,z.js