1. 基本使用

gulp作为当下最流行的前端构建系统,其核心特点就是高效易用。使用gulp的过程非常简单,大体过程就是在项目中安装gulp依赖, 然后创建gulpfile文件来配置gulp

npm init
npm install gulp --save

gulpfile.js

exports.foo = () => {
    console.log('foo');
}

这样就创建了一个foogulp任务,可以运行一下gulp foo

npx gulp foo

gulp中所有的任务都是异步任务,需要标记任务完成,可以在函数中接收一个函数,调用这个函数就表示完成了。

exports.foo = (done) => {
    console.log('foo');
    done(); // 结束标识
}

如果导出的是default任务会作为gulp的默认任务出现,运行gulp的时候可以不指定任务名称。在gulp4.x以前注册gulp任务需要使用task方法,不过这种方式已经不推荐了。

const gulp = require('gulp');
gulp.task('foo', done => {
    console.log('任务')
    done();
})

2. 组合任务

可以通过gulp模块提供的seriesparallel组合任务。series是一个函数,可以接收任意个数的参数,每个参数就是一个任务,series会自动的按顺序依次执行这些任务。

const { series } = require('gulp');

const t1 = done => {
    console.log('任务1')
    done();
}
const t2 = done => {
    console.log('任务2')
    done();
}
const t3 = done => {
    console.log('任务3')
    done();
}

exports.foo = series(t1, t2, t3);
npx gulp foo

series组合的是串行的任务,也就是按顺序执行的,如果想要组合并行任务可以使用parallel,用法和series相同。任务组合还是非常有用的,比如说编译jscss,可以通过并行的方式分别编译。

3. 异步任务

在调用一个异步任务的时候是无法明确这个任务是否执行完毕的,一般都是通过回调函数来解决。在glup中可以通过回调函数来解决异步任务,也就是done函数。可以给done函数中传入一个错误信息来阻止后面的任务执行。

exports.foo = (done) => {
    console.log('foo');
    done(new Error('失败了'); // 结束标识
}

gulp也支持Promise的方式,就是在任务中返回Promise,可以返回resolve或者reject

exports.foo = () => {
    console.log('foo');
    return Promise.resolve()
}

当然asyncawait也是可以的,任务定义为async类型的函数就可以使用await了。

exports.foo = async () => {
    // await ....
    console.log('foo');
    return Promise.resolve()
}

如果读取文件可以使用stream的方式, 返回stream就可以了。

exports.foo = () => {
    console.log('foo');
    const readStream = fs.createReadStream('a.json');
    const writeStream = fs.createWriteStream('b.json');
    readStream.pipe(writeStream);
    return readStream; // 相当于注册end事件调用done
}

4. 核心原理

构建过程基本是将文件读取出来经过转换再写入到对应的位置,在以前这都是手动去做的有了gulp以后就可以通过代码自动化执行。

const fs = require('fs');
const { Transform } = require('stream');
exports.default = () => {
    // 创建文件读取流
    const read = fs.createReadStream('a.css');
    // 文件写入流
    const write = fs.createWriteStream('b.css');
    // 文件转换
    const transform = new Transform({
        transform: (chunk, encoding, callback) => {
            // chunk是读取到的内容,也就是流
            // 使用正则删除空格删除注释
            const input = chunk.toString();
            const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '');
            callback(output);
        }
    })
    // 读取到的文件写入到对应文件
    read.pipe(transform).pipe(write);
    return read;
}

gulp官方的定义就是基于流的构建系统,gulp实现的是一种管道的概念。

5. 文件操作

gulp有自己的读取流和写入流,相比较node来说更好用一些。

const { src, dest } = require('gulp');
const cleanCss = require('gulp-clean-css');
const rename = require('gulp-rename');

export.default = () => {
    return src('src/a.css').pipe(cleanCss()).pipe(rename({ extname: '.min.css'})).pipe(dest('dist'));
}

这样会将src/a.css文件写入到dist文件夹下。

const { src, dest } = require('gulp');
const cleanCss = require('gulp-clean-css')

export.default = () => {
    return src('src/*.css').pipe(cleanCss()).pipe(rename({ extname: '.min.css'})).pipe(dest('dist'));
}

6. 样式编译

const { src, dest } = require('gulp');
const sass = require('gulp-sass');

const style = () => {
    return src('src/*.css', { base: 'src'}).pipe(sass({outputStyle: 'expanded'})).pipe(dest('dist'));
}

module.exports = {
    style
}

7. 脚本编译

const { src, dest, parallel, series } = require('gulp');

const del = require('del');
const sass = require('gulp-sass');
const babel = require('gulp-babel');
const swig = require('gulp-swig');
const imagemin = require('gulp-imagemin');

// 删除dist
const clean = () => {
    return del(['dist']);
}
// 样式
const style = () => {
    return src('src/*.css', { base: 'src'}).pipe(sass({outputStyle: 'expanded'})).pipe(dest('dist'));
}
// js
const script = () => {
    return src('src/*.js', { base: 'src'}).pipe(babel({presets: ['@babel/preset-env']})).pipe(dest('dist'));
}
// html swig中可以使用传入的参数
const page = () => {
    return src('src/*.html', { base: 'src'}).pipe(swig({data: { date: new Date()})).pipe(dest('dist'));
}
// image
const image = () => {
    return src('src/images/**', { base: 'src'}).pipe(imagemin({})).pipe(dest('dist'));
}
// font
const font = () => {
    return src('src/fonts/**', { base: 'src'}).pipe(imagemin({})).pipe(dest('dist'));
}
// extra copy public file
const extra = () => {
    return src('public/**', { base: 'public'}).pipe(dest('dist'));
}

const compile = parallel(style, script, page, image, font);

const build = series(clean, parallel(compile, extra));

module.exports = {
   build
}

8. 自动加载插件

手动方式载入插件require会越来越多不利于后期维护,可以通过gulp-load-plugin提供的插件自动载入使用的插件。

// 自动载入插件
const loadPlugins = require('gulp-load-plugins');
const plugins = loadPlugins();

用法很简单,会自动把gulp-删除,后面的名称变为驼峰命名,比如gulp-sass可以写成plugins.sass

const { src, dest, parallel, series } = require('gulp');

const del = require('del');

// 自动载入插件
const loadPlugins = require('gulp-load-plugins');
const plugins = loadPlugins();

// 删除dist
const clean = () => {
    return del(['dist']);
}
// 样式
const style = () => {
    return src('src/*.css', { base: 'src'}).pipe(plugins.sass({outputStyle: 'expanded'})).pipe(dest('dist'));
}
// js
const script = () => {
    return src('src/*.js', { base: 'src'}).pipe(plugins.babel({presets: ['@babel/preset-env']})).pipe(dest('dist'));
}
// html swig中可以使用传入的参数
const page = () => {
    return src('src/*.html', { base: 'src'}).pipe(plugins.swig({data: { date: new Date()})).pipe(dest('dist'));
}
// image
const image = () => {
    return src('src/images/**', { base: 'src'}).pipe(plugins.imagemin({})).pipe(dest('dist'));
}
// font
const font = () => {
    return src('src/fonts/**', { base: 'src'}).pipe(plugins.imagemin({})).pipe(dest('dist'));
}
// extra copy public file
const extra = () => {
    return src('public/**', { base: 'public'}).pipe(dest('dist'));
}

const compile = parallel(style, script, page, image, font);

const build = series(clean, parallel(compile, extra));

module.exports = {
   build
}

9. 开发服务器

开发服务器可以配合构建任务在代码修改过后实现自动更新,提高开发阶段的效率。他依赖的是browser-sync的模块,需要提前安装。支持代码热更新的功能。注意他并不是glup的插件,这里只是引用并且管理它而已。

const browserSync = require('browser-sync');
const bs = browserSync.create();

const serve = () => {
    bs.init({ // 初始化
        port: 8080,
        open: false,
        files: 'dist/**',
        server: {
            baseDir: 'dist',
            routes: {
                '/node_modules': 'node_modules',
            }
        }
    });
}

module.exports = {
    serve
}

routes是将请求转发到对应位置,优先于baseDir,如果不存在才会走baseDir

init可以配置很多参数,portnotifyopenfiles等。files是监听的通配符,指定哪些文件更细就会发生重新渲染。

10. 监视变化构建优化

要实现src目录下的文件更改重新编译,需要借助glup提供的watch功能。他会监视一个路径的通配符,然后根据文件变化执行一个任务,只需要监视构建任务的路径就可以。

const { src, dest, parallel, series, watch } = require('gulp');

// 自动载入插件
const loadPlugins = require('gulp-load-plugins');
const plugins = loadPlugins();

// 样式
const style = () => {
    return src('src/*.css', { base: 'src'}).pipe(plugins.sass({outputStyle: 'expanded'})).pipe(dest('dist'));
}

const serve = () => {
    watch('src/*.css', style);
}

通过watch可以触发源代码修改过后自动编译到distdist变更再重新渲染浏览器。

一般情况下开发阶段对于图片压缩文件压缩基本不需要做,只需要在上线前做一次就可以了,所以imagespublic这类的文件直接指定路径就可以了不要打包。

const { src, dest, parallel, series, watch } = require('gulp');

// 自动载入插件
const loadPlugins = require('gulp-load-plugins');
const plugins = loadPlugins();

const browserSync = require('browser-sync');
const bs = browserSync.create();

// 样式
const style = () => {
    return src('src/*.css', { base: 'src'}).pipe(plugins.sass({outputStyle: 'expanded'})).pipe(dest('dist'));
}

const serve = () => {
    watch('src/*.css', style); // 构建

    watch('src/images/**', 'public/**', bs.reload); // 变化更新

    bs.init({ // 初始化
        port: 8080,
        open: false,
        files: 'dist/**',
        server: {
            baseDir: ['dist', 'src', 'public'],
            routes: {
                '/node_modules': 'node_modules',
            }
        }
    });
}

module.exports = {
    serve
}

11. useref

假设我们html中引入了node_modules文件夹中的资源,开发环境是没有问题的,但是build的正式环境是有问题的。在开发环境配置了routes来映射node_moudles,正式环境也需要一些配置。

useref会自动处理html中的构建注释,也就是可以把下面注释中的文件打包到指定的位置,如果多个标识了同一个位置,就会合并。

<!-- build:css dist/a.css -->
<link ref="stylesheet" href="/node_modules/bootstrap/dist/bootstrap.css" />
<!-- endbuild -->

这种方式会更加简单,压缩合并都可以完成。这里使用gulp-useref插件,监听的是打包过后的文件。

const useref = () => {
    return src('dist/*.html', {base: 'dist'}).pipe(plugins.useref({searchPath: ['dist', '.']})).pipe(dest('dist'));
}

module.exports = {
    useref
}

这样他会将构建注释去掉,将构建注释中的内容进行合并,替换html中的引用。

12. 文件压缩

需要压缩的文件有3种,htmljscss。他们都是useref创建出来的。所以useref管道中会有三种文件类型,需要分别做不同的压缩工作。

gulp-uglify是压缩js的,gulp-clean-css是压缩css的,gulp-htmlnin是压缩html的。

const useref = () => {
    return src('dist/*.html', {base: 'dist'})
    .pipe(plugins.useref({searchPath: ['dist', '.']}))
    .pipe(plugins.if(/\.js$/, plugins.uglify()))
    .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
    .pipe(plugins.if(/\.html$/, plugins.htmlmin({
        collapseWhitespace: true,
        minifyCss: true,
        minifyJs: true
    })))
    .pipe(dest('release'));
}

module.exports = {
    useref
}

13. 重新规划构建过程

前面打包的时候通过useref进行处理,src打包到build目录之后userefbuild转换到release目录中。在这里build就是一个中间的媒介也就是一个临时目录。这里可以整理下也就是替换文件夹名称。将build改成temp,将release改成build

const { src, dest, parallel, series, watch } = require('gulp');

// 自动载入插件
const loadPlugins = require('gulp-load-plugins');
const plugins = loadPlugins();

const browserSync = require('browser-sync');
const bs = browserSync.create();

// 样式
const style = () => {
    return src('src/*.css', { base: 'src'}).pipe(plugins.sass({outputStyle: 'expanded'})).pipe(dest('temp'));
}

const serve = () => {
    watch('src/*.css', style); // 构建

    watch('src/images/**', 'public/**', bs.reload); // 变化更新

    bs.init({ // 初始化
        port: 8080,
        open: false,
        files: 'temp/**',
        server: {
            baseDir: ['temp', 'src', 'public'],
            routes: {
                '/node_modules': 'node_modules',
            }
        }
    });
}
const useref = () => {
    return src('temp/*.html', {base: 'temp'})
    .pipe(plugins.useref({searchPath: ['temp', '.']}))
    .pipe(plugins.if(/\.js$/, plugins.uglify()))
    .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
    .pipe(plugins.if(/\.html$/, plugins.htmlmin({
        collapseWhitespace: true,
        minifyCss: true,
        minifyJs: true
    })))
    .pipe(dest('build'));
}

module.exports = {
    useref,
    serve
}

转载须知

如转载必须标明文章出处文章名称文章作者,格式如下:

转自:【致前端 - zhiqianduan.com】 gulp打包工具  "隐冬"
请输入评论...