gulp相关知识

gulp基础

什么是gulp

  • gulp

    是可以自动化执行任务的工具 在平时开发的流程里面,一定有一些任务需要手工重复得执行,比如:

    • 把文件从开发目录拷贝到生产目录

    • 把多个 JS 或者 CSS 文件合并成一个文件

    • 对JS文件和CSS进行压缩

    • 把sass或者less文件编译成CSS

    • 压缩图像文件

    • 创建一个可以实时刷新页面内容的本地服务器

      只要你觉得有些动作是要重复去做的,就可以把这些动作创建成一个gulp任务,然后在指定的条件下自动执行

gulp特点

  • 易于使用 通过代码优于配置的策略,Gulp 让简单的任务简单,复杂的任务可管理
  • 快速构建 利用 node.js 流的威力,你可以快速构建项目并减少频繁的 IO 操作
  • 高质量的插件 Gulp 严格的插件指南确保插件如你期望的那样简洁地工作
  • 易于学习 通过最少的 API,掌握 gulp 毫不费力,构建工作尽在掌握

安装gulp

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

异步任务和组合任务

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

gulp核心API

gulp.src()

  • gulp.src()方法正是用来获取流的
  • 注意这个流里的内容不是原始的文件流,而是一个虚拟文件对象流
  • globs 参数是文件匹配模式(类似正则表达式),用来匹配文件路径(包括文件名),当然这里也可以直接指定某个具体的文件路径。当有多个匹配模式时,该参数可以为一个数组
  • options 为可选参数
gulp.src(globs[, options])

gulp.dest()

  • gulp.dest()
    

    是用来向硬盘写入文件的

    • path 为写入文件的路径
    • options 为一个可选的参数对象
  • gulp.dest()传入的路径参数只能用来指定要生成的文件的目录,而不能指定生成文件的文件名 它生成文件的文件名使用的是导入到它的文件流自身的文件名 所以生成的文件名是由导入到它的文件流决定的

  • gulp.dest(path)生成的文件路径是我们传入的path参数后面再加上gulp.src()中有通配符开始出现的那部分路径

  • 通过指定gulp.src()方法配置参数中的base属性,我们可以更灵活的来改变gulp.dest()生成的文件路径

gulp.dest(path[,options])

pipe

gulpfile.js

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

gulp实战

安装

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

编译样式

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;

编译脚本

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;

编译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>
        <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>
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>
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>
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;

编译任务

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;

压缩图片

安装
npm install gulp-imagemin --save-dev
npm install imagemin-jpegtran imagemin-svgo imagemin-gifsicle imagemin-optipng --save-dev
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;

拷贝静态文件

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;

删除输出目录

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;

自动加载插件

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;

开发服务器

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');
+});

监听文件变化

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;

合并和压缩

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>
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
+}

glob相关

gulp内部使用了node-glob模块来实现其文件匹配功能

glob规则

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

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

环节实现

实现gulp

安装依赖

npm install fs-extra undertaker vinyl-fs --save

gulpfile.js

gulpfile.js

const { series, parallel } = require('gulp');
const defaultTask = (done) => {
    console.log('defaultTask');
    done();
}

const oneTask = (done) => {
    setTimeout(() => {
        console.log('oneTask');
        done();
    }, 1000);
}
const twoTask = (done) => {
    setTimeout(() => {
        console.log('twoTask');
        done();
    }, 1000);
}
const threeTask = (done) => {
    setTimeout(() => {
        console.log('threeTask');
        done();
    }, 1000);
}

const seriesTask = series(oneTask, twoTask, threeTask);
const parallelTask = parallel(oneTask, twoTask, threeTask);
exports.default = defaultTask;
exports.series = seriesTask;
exports.parallel = parallelTask;

package.json

{
  "main": "lib/index.js",
  "bin": {
    "gulp4": "./bin/gulp4.js"
  }
}

bin\gulp4.js

bin\gulp4.js

#!/usr/bin/env node
const path = require('path');
const gulpInst = require('../lib');
const logEvents = require('./logEvents');
const registerExports = require('./register-exports');
logEvents(gulpInst);
const taskName = process.argv[2];
const toRun = taskName || 'default';
const configPath = path.join(process.cwd(), 'gulpfile.js');
console.log(`Using gulpfile ${configPath}`);
const exported = require(configPath);
registerExports(gulpInst, exported);
gulpInst.parallel(toRun)(() => { console.log('done') });

bin\logEvents.js

bin\logEvents.js

function logEvents(gulpInst) {
    gulpInst.on('start', function (evt) {
        console.log(`Starting ${evt.name} ...`);
    });
    gulpInst.on('stop', function (evt) {
        console.log(`Finished ${evt.name} after ${evt.duration[0]} ms`);
    });
}
module.exports = logEvents;

bin\register-exports.js

bin\register-exports.js

function registerExports(gulpInst, tasks) {
    let taskNames = Object.keys(tasks);
    taskNames.forEach(taskName => {
        gulpInst.task(taskName, tasks[taskName]);
    });
}
module.exports = registerExports;

lib\index.js

lib\index.js

const util = require('util');
const Undertaker = require('undertaker');
function Gulp() {
    Undertaker.call(this);
    this.task = this.task.bind(this);
    this.series = this.series.bind(this);
    this.parallel = this.parallel.bind(this);
}
util.inherits(Gulp, Undertaker);
const inst = new Gulp();
module.exports = inst;

实现undertaker

undertaker 是一个用于管理任务的 Node.js 模块,主要用于构建自动化工作流,特别是在构建工具如 Gulp 中非常常用。undertaker 提供了一种定义、组合和运行任务的机制,允许开发者创建复杂的任务序列和并行任务处理。undertaker 通常与 Gulp 一起使用,提供了强大的任务管理和自动化构建能力。

  • https://www.npmjs.com/package/undertaker

  • 主要功能

    • 任务定义:使用 undertaker 可以定义单个任务,每个任务都是一个函数。
    • 任务组合:支持将多个任务组合成一个任务,任务可以是串行(顺序执行)或者并行(同时执行)。
    • 任务运行:可以运行单个任务或者任务组合,并且可以监听任务的开始、完成和错误等事件。

npm使用示例

var fs = require('fs');
var Undertaker = require('undertaker');

var taker = new Undertaker();

taker.task('task1', function(cb){
  // do things

  cb(); // when everything is done
});

taker.task('task2', function(){
  return fs.createReadStream('./myFile.js')
    .pipe(fs.createWriteStream('./myFile.copy.js'));
});

taker.task('task3', function(){
  return new Promise(function(resolve, reject){
    // do things

    resolve(); // when everything is done
  });
});

// 串行执行任务
taker.task('combined', taker.series('task1', 'task2'));

// 并行执行任务
taker.task('all', taker.parallel('combined', 'task3'));

实现

lib\index.js

lib\index.js

const util = require('util');
+const Undertaker = require('./undertaker');
function Gulp() {
    Undertaker.call(this);
    this.task = this.task.bind(this);
    this.series = this.series.bind(this);
    this.parallel = this.parallel.bind(this);
}
util.inherits(Gulp, Undertaker);
const inst = new Gulp();
module.exports = inst;
undertaker.js

lib\undertaker.js

let { inherits } = require('util');
let EventEmitter = require('events')
function Undertaker() {
    EventEmitter.call(this);
    this._tasks = {};
}
inherits(Undertaker, EventEmitter);
function task(name, fn) {
    this._tasks[name] = fn;
}
function series() {
    let args = Array.from(arguments);
    let fn = buildSeries(args);
    return fn.bind(this);
}
function parallel() {
    let args = Array.from(arguments);
    let fn = buildParallel(args);
    return fn.bind(this);;
}
function run(taskName, done) {
    let fn = this._tasks[taskName];
    fn(done);
}
Undertaker.prototype.task = task;
Undertaker.prototype.series = series;
Undertaker.prototype.parallel = parallel;
Undertaker.prototype.run = run;
function buildSeries(values) {
    function series(done) {
        let length = values.length;
        let idx = 0;
        let results = [];
        const next = (idx) => {
            let value = values[idx];
            if (typeof value !== 'function') {
                value = this._tasks[value];
            }
            let startHr = process.hrtime();
            this.emit('start', { name: value.name });
            value((err, result) => {
                this.emit('stop', { name: value.name, duration: process.hrtime(startHr) });
                results[idx] = result;
                if (++idx >= length) {
                    done(err, results);
                } else {
                    next(idx);
                }
            });
        }
        next(idx);
    }
    return series;
}
function buildParallel(values) {
    function parallel(done) {
        let length = values.length;
        let count = length;
        let results = [];
        const next = (idx) => {
            let value = values[idx];
            if (typeof value !== 'function') {
                value = this._tasks[value];
            }
            let startHr = process.hrtime();
            this.emit('start', { name: value.name });
            value((err, result) => {
                this.emit('stop', { name: value.name, duration: process.hrtime(startHr) });
                results[idx] = result;
                if (--count === 0) {
                    done(err, results);
                }
            });
        }
        for (idx = 0; idx < length; idx++) {
            next(idx);
        }
    }
    return parallel;
}
module.exports = Undertaker;

使用流操作

gulpfile.js

const { src, dest } = require('gulp');
const defaultTask = () => {
    return src('src/scripts/**/*.js').pipe(dest('dist'));
}

exports.default = defaultTask;
# 执行
gulp

vinyl-fs

vinyl-fs基础

glob
  • gulp内部使用了node-glob模块来实现其文件匹配功能
匹配符说明
匹配文件路径中的0个或多个字符,但不会匹配路径分隔符
星星匹配路径中的0个或多个目录及其子目录
?匹配文件路径中的一个字符(不会匹配路径分隔符)
[…]匹配方括号中出现的字符中的任意一个,当方括号中第一个字符为^或!时,则表示不匹配方括号中出现的其他字符中的任意一个
!(pattern1/pattern2/pattern3)匹配任何与括号中给定的任一模式都不匹配的
?(pattern1/pattern2/pattern3)匹配括号中给定的任一模式0次或1次
+(pattern1/patter2n/pattern3)匹配括号中给定的任一模式至少1次
(pattern1/pattern2/pattern3)匹配括号中给定的任一模式0次或多次
@(pattern1/pattern2/pattern3)匹配括号中给定的任一模式1次

glob-stream.js

const { Readable } = require('stream');
let { inherits } = require('util');
let { Glob } = require('glob');
var globParent = require('glob-parent');
var toAbsoluteGlob = require('to-absolute-glob');
function GlobStream(glob, opt = {}) {
    opt.cwd = opt.cwd || process.cwd();
    Readable.call(this, { objectMode: true });
    let absoluteGlob = toAbsoluteGlob(glob, opt);
    let basePath = globParent(absoluteGlob);
    let globber = new Glob(absoluteGlob, opt);
    this._globber = globber;
    globber.on('match', (filepath) => {
        let obj = {
            cwd: opt.cwd,
            base: basePath,
            path: filepath
        };
        this.push(obj);
    });
    globber.once('end', () => {
        this.push(null);
    });
}
inherits(GlobStream, Readable);

GlobStream.prototype._read = function () {
    this._globber.resume();
};
module.exports = GlobStream;
let GlobStream = require('./glob-stream');
const glob = 'src/scripts/**/*.js';
let gs = new GlobStream(glob);
gs.on('data', (data) => {
    console.log(data);
});
vinyl-fs介绍

vinyl-fs 是一个用于文件操作的 Node.js 模块,它是 Gulp 生态系统的一部分。vinyl-fs 提供了一种使用流(streams)来读取、写入和操作文件的方式。

  • 主要功能

    • 文件读取:从文件系统读取文件并将它们转换为 Vinyl 文件对象。
    • 文件写入:将 Vinyl 文件对象写回到文件系统中。
    • 文件操作:提供了一些常见的文件操作方法,如重命名、过滤、匹配等。
  • 使用 vinyl-fs 来读取和写入文件示例

    const vfs = require('vinyl-fs');
    const through = require('through2');
    
    function transformFile() {
      return through.obj(function (file, enc, cb) {
        // 在这里可以对文件内容进行修改
        if (file.isBuffer()) {
          const content = file.contents.toString(enc);
          const transformedContent = content.toUpperCase(); // 将文件内容转为大写
          file.contents = Buffer.from(transformedContent, enc);
        }
        cb(null, file);
      });
    }
    
    // 读取src目录下的所有txt文件,转换为大写后写入到dist目录
    vfs.src('src/**/*.txt')
      .pipe(transformFile())
      .pipe(vfs.dest('dist'));
    
    
  • 在这个示例中:

    • vfs.src 方法读取 src 目录下的所有 .txt 文件,并将它们转换为 Vinyl 文件对象。
    • transformFile 函数定义了一个流处理,接收文件并将其内容转换为大写。
    • vfs.dest 方法将处理后的文件写入到 dist 目录中。
vinyl文件对象
  • gulp.src中这个流里的内容不是原始的文件流,而是一个虚拟文件对象流,这个虚拟文件对象中存储着原始文件的路径、文件名和内容等信息 vinyl
  • Vinyl 是一种虚拟文件格式,表示文件系统中的文件。它包含文件的路径、内容、状态等信息。vinyl-fs 使用 Vinyl 文件对象来抽象文件系统的操作,使得文件操作可以在流处理中完成。
var File = require('vinyl');
var indexFile = new File({
    cwd: "/",//当前路径
    base: "/test/",//文件名
    path: "/test/index.js",//路径
    contents: new Buffer("hs")//文件内容
});

console.log(File.isVinyl(indexFile));//是否是vinyl
console.log(indexFile.isBuffer());//内容是否是Buffer
console.log(indexFile.isStream());//内容是否是Stream
wrap-vinyl.js
  • through2内部仅是封装了Transform的构造函数

image-20240628103556989

wrap-vinyl.js

var File = require('vinyl');
var through = require('through2');
function wrapVinyl() {
    function wrapFile(globFile, encoding, callback) {
        var file = new File(globFile);
        callback(null, file);
    }
    return through.obj(wrapFile);
}
module.exports = wrapVinyl;
let GlobStream = require('./glob-stream');
let wrapVinyl = require('./wrap-vinyl');
const glob = 'src/scripts/**/*.js';
let gs = new GlobStream(glob);
gs.pipe(wrapVinyl()).on('data', (data) => {
    console.log(data);
});

实现vinyl-fs

vinyl-fs\index.js

lib\vinyl-fs\index.js

const src = require('./lib/src');
const dest = require('./lib/dest');
module.exports = {
    src,
    dest
};
src\index.js

lib\vinyl-fs\lib\src\index.js

var gs = require('./glob-stream');
var readContents = require('./read-contents');
var wrapVinyl = require('./wrap-vinyl');
function src(glob) {
    let gsStream = gs(glob);
    let vinylStream = gsStream.pipe(wrapVinyl());
    let contentsStream = vinylStream.pipe(readContents());
    return contentsStream;
}
module.exports = src;
glob-stream.js

lib\vinyl-fs\lib\src\glob-stream.js

let { Readable } = require('readable-stream');
let { inherits } = require('util');
let { Glob } = require('glob');
var globParent = require('glob-parent');
var toAbsoluteGlob = require('to-absolute-glob');
inherits(GlobStream, Readable);
GlobStream.prototype._read = function () {
    this._globber.resume();
};
function globStream(glob, opt = {}) {
    opt.cwd = opt.cwd || process.cwd();
    return new GlobStream(glob, opt);
}
function GlobStream(glob, opt) {
    Readable.call(this, { objectMode: true });
    let absoluteGlob = toAbsoluteGlob(glob, opt);
    let basePath = globParent(absoluteGlob);
    let globber = new Glob(absoluteGlob, opt);
    this._globber = globber;
    globber.on('match', (filepath) => {
        let obj = {
            cwd: opt.cwd,
            base: basePath,
            path: filepath
        };
        this.push(obj)
    });
    globber.once('end', () => {
        this.push(null);
    });
}
module.exports = globStream;
read-contents.js

lib\vinyl-fs\lib\src\read-contents.js

let fs = require('fs');
let through = require('through2');
function readContents() {
    function readFile(file, encoding, callback) {
        fs.readFile(file.path, encoding, (err, data) => {
            file.contents = Buffer.from(data);
            callback(null, file);
        });
    }
    return through.obj(readFile);
}
module.exports = readContents;
wrap-vinyl.js

lib\vinyl-fs\lib\src\wrap-vinyl.js

var File = require('vinyl');
var through = require('through2');
function wrapVinyl() {
    function wrapFile(globFile, encoding, callback) {
        var file = new File(globFile);
        callback(null, file);
    }
    return through.obj(wrapFile);
}
module.exports = wrapVinyl;
dest\index.js

lib\vinyl-fs\lib\dest\index.js

const writeContents = require('./write-contents');
function dest(outFolder) {
    return writeContents(outFolder);
}
module.exports = dest;
write-contents.js

lib\vinyl-fs\lib\dest\write-contents.js

const fs = require('fs-extra');
const path = require('path');
var through = require('through2');
function writeContents(outFolder) {
    function writeFile(file, encoding, callback) {
        var basePath = path.resolve(file.cwd, outFolder);
        var writePath = path.resolve(basePath, file.relative);
        file.path = writePath;
        fs.ensureDir(path.dirname(writePath), (err) => {
            fs.writeFile(file.path, file.contents, encoding, callback);
        });
    }
    return through.obj(writeFile);
}
module.exports = writeContents;

实现插件

gulpfile.js

gulpfile.js

const { src, dest } = require('gulp4');
var gulpPrefixer = require('./gulp-prefixer');
var gulpBabel = require('./gulp-babel');
const defaultTask = () => {
    return src('src/scripts/**/*.js')
        .pipe(gulpPrefixer('/**prepended**/\n'))
        .pipe(gulpBabel({ presets: ["@babel/preset-env"] }))
        .pipe(dest('dist'));
}
exports.default = defaultTask;

gulp-prefixer.js

gulp-prefixer.js

var through = require('through2');
function gulpPrefixer(prefixText) {
    prefixText = Buffer.from(prefixText);
    var stream = through.obj(function (file, enc, next) {
        if (file.isBuffer()) {
            file.contents = Buffer.concat([prefixText, file.contents]);
        }
        this.push(file);
        next();
    });
    return stream;
};
module.exports = gulpPrefixer;

gulp-babel.js

gulp-babel.js

var through = require('through2');
const babel = require('@babel/core');
function gulpBabel(options) {
    var stream = through.obj(function (file, enc, next) {
        const { code } = babel.transformSync(file.contents, options);
        file.contents = Buffer.from(code);
        this.push(file);
        next();
    });
    return stream;
};
module.exports = gulpBabel;
  • 40
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值