Gulp 作为当下最流行的前端构建系统,其核心特点就是高效、易用。
基本使用
yarn init -y
yarn add gulp --dev
code gulpfile.js
Gulp 最新版需要使用 done 标识任务结束。
// gulp 入口文件exports.foo = done => {console.log('foo task working');done(); // 标识任务完成}exports.default = done => {console.log('default task working');done();}
gulp 4.0 之前注册 Gulp 任务需要通过 Gulp 模块内部的方法实现。Gulp 4.0 以后保留了这种方式。
const gulp = require('gulp');gulp.task('bar', done => {console.log('bar task working');done();});
组合任务
const { series, parallel } = require('gulp');const task1 = done => {setTimeout(() => {console.log('task1 working');done();}, 1000);}const task2 = done => {setTimeout(() => {console.log('task2 working');done();}, 1000);}const task3 = done => {setTimeout(() => {console.log('task3 working');done();}, 1000);}exports.foo = series(task1, task2, task3); // 串行执行exports.bar = parallel(task1, task2, task3); // 并行执行
构建 JS、CSS 时可以使用 parallel。代码部署可以使用 series,部署前必须构建完毕。
异步任务
回调方式
exports.callback = done => {console.log('callback task');done();}exports.callback = done => {console.log('callback task');done(new Error('task failed'));}
Promise 方式
exports.promise = () => {console.log('promise task');return Promise.resolve();}exports.promise = () => {console.log('promise task');return Promise.reject(new Error('task failed'));}
async/await 方式
const timeout = time => {return new Promise(resolve => {setTimeout(resolve, time);});}exports.async = async () => {await timeout(1000);console.log('async task');}
stream 方式
exports.stream = () => {const readStream = fs.createReadStream('package.json');const wirteStream = fs.createWriteStream('temp.txt');readStream.pipe(wirteStream);return readStream;}
构建过程
输入(读取流) => 加工(转换流) => 输出(写入流)
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css *//* Document========================================================================== *//*** 1. Correct the line height in all browsers.* 2. Prevent adjustments of font size after orientation changes in iOS.*/html {line-height: 1.15; /* 1 */-webkit-text-size-adjust: 100%; /* 2 */}/* Sections========================================================================== *//*** Remove the margin in all browsers.*/body {margin: 0;}/*** Render the `main` element consistently in IE.*/main {display: block;}/*** Correct the font size and margin on `h1` elements within `section` and* `article` contexts in Chrome, Firefox, and Safari.*/h1 {font-size: 2em;margin: 0.67em 0;}/* Grouping content========================================================================== *//*** 1. Add the correct box sizing in Firefox.* 2. Show the overflow in Edge and IE.*/hr {box-sizing: content-box; /* 1 */height: 0; /* 1 */overflow: visible; /* 2 */}/*** 1. Correct the inheritance and scaling of font size in all browsers.* 2. Correct the odd `em` font sizing in all browsers.*/pre {font-family: monospace, monospace; /* 1 */font-size: 1em; /* 2 */}/* Text-level semantics========================================================================== *//*** Remove the gray background on active links in IE 10.*/a {background-color: transparent;}/*** 1. Remove the bottom border in Chrome 57-* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.*/abbr[title] {border-bottom: none; /* 1 */text-decoration: underline; /* 2 */text-decoration: underline dotted; /* 2 */}/*** Add the correct font weight in Chrome, Edge, and Safari.*/b,strong {font-weight: bolder;}/*** 1. Correct the inheritance and scaling of font size in all browsers.* 2. Correct the odd `em` font sizing in all browsers.*/code,kbd,samp {font-family: monospace, monospace; /* 1 */font-size: 1em; /* 2 */}/*** Add the correct font size in all browsers.*/small {font-size: 80%;}/*** Prevent `sub` and `sup` elements from affecting the line height in* all browsers.*/sub,sup {font-size: 75%;line-height: 0;position: relative;vertical-align: baseline;}sub {bottom: -0.25em;}sup {top: -0.5em;}/* Embedded content========================================================================== *//*** Remove the border on images inside links in IE 10.*/img {border-style: none;}/* Forms========================================================================== *//*** 1. Change the font styles in all browsers.* 2. Remove the margin in Firefox and Safari.*/button,input,optgroup,select,textarea {font-family: inherit; /* 1 */font-size: 100%; /* 1 */line-height: 1.15; /* 1 */margin: 0; /* 2 */}/*** Show the overflow in IE.* 1. Show the overflow in Edge.*/button,input { /* 1 */overflow: visible;}/*** Remove the inheritance of text transform in Edge, Firefox, and IE.* 1. Remove the inheritance of text transform in Firefox.*/button,select { /* 1 */text-transform: none;}/*** Correct the inability to style clickable types in iOS and Safari.*/button,[type="button"],[type="reset"],[type="submit"] {-webkit-appearance: button;}/*** Remove the inner border and padding in Firefox.*/button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner {border-style: none;padding: 0;}/*** Restore the focus styles unset by the previous rule.*/button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring {outline: 1px dotted ButtonText;}/*** Correct the padding in Firefox.*/fieldset {padding: 0.35em 0.75em 0.625em;}/*** 1. Correct the text wrapping in Edge and IE.* 2. Correct the color inheritance from `fieldset` elements in IE.* 3. Remove the padding so developers are not caught out when they zero out* `fieldset` elements in all browsers.*/legend {box-sizing: border-box; /* 1 */color: inherit; /* 2 */display: table; /* 1 */max-width: 100%; /* 1 */padding: 0; /* 3 */white-space: normal; /* 1 */}/*** Add the correct vertical alignment in Chrome, Firefox, and Opera.*/progress {vertical-align: baseline;}/*** Remove the default vertical scrollbar in IE 10+.*/textarea {overflow: auto;}/*** 1. Add the correct box sizing in IE 10.* 2. Remove the padding in IE 10.*/[type="checkbox"],[type="radio"] {box-sizing: border-box; /* 1 */padding: 0; /* 2 */}/*** Correct the cursor style of increment and decrement buttons in Chrome.*/[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button {height: auto;}/*** 1. Correct the odd appearance in Chrome and Safari.* 2. Correct the outline style in Safari.*/[type="search"] {-webkit-appearance: textfield; /* 1 */outline-offset: -2px; /* 2 */}/*** Remove the inner padding in Chrome and Safari on macOS.*/[type="search"]::-webkit-search-decoration {-webkit-appearance: none;}/*** 1. Correct the inability to style clickable types in iOS and Safari.* 2. Change font properties to `inherit` in Safari.*/::-webkit-file-upload-button {-webkit-appearance: button; /* 1 */font: inherit; /* 2 */}/* Interactive========================================================================== *//** Add the correct display in Edge, IE 10+, and Firefox.*/details {display: block;}/** Add the correct display in all browsers.*/summary {display: list-item;}/* Misc========================================================================== *//*** Add the correct display in IE 10+.*/template {display: none;}/*** Add the correct display in IE 10.*/[hidden] {display: none;}
const fs = require('fs');
const { Transform } = require('stream');
exports.default = () => {
const read = fs.createReadStream('normalize.css');
const wirte = fs.createWriteStream('dist/normalize.min.css');
// 代码转换
const transform = new Transform({
transform: (chunk, encoding, callback) => {
const input = chunk.toString();
const output = input.replace(/\s+/g, '').replace(/\/\*.+?\*\//g, '');
callback(null, output);
}
})
read
.pipe(transform)
.pipe(wirte);
return read;
}
Gulp 官方定义就是 The streaming build system,基于流的构建系统。
Gulp 希望实现一个构建管道的概念,这样在后续扩展插件时就有很统一的方式。
文件操作 API
Gulp 已经提供的文件操作 API,相对于 node API,它更强大,更容易使用。
转换 CSS 代码
yarn add gulp-clean-css --dev
修改扩展名
yarn add gulp-rename --dev
gulpfile.js
const { src, dest } = require('gulp');
const cleanCss = require('gulp-clean-css');
const rename = require('gulp-rename');
exports.default = () => {
return src('src/*.css')
.pipe(cleanCss())
.pipe(rename({ extname: '.min.css' }))
.pipe(dest('dist'));
}
案例
yarn add gulp --dev
code gulpfile.js
样式编译
yarn add gulp-sass --dev
sass 只会转换非 为前缀的代码。以 为前缀的文件会被忽略掉。
const { src, dest } = require('gulp');
const sass = require('gulp-sass');
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}
module.exports = {
style
}
脚本编译
yarn add gulp-babel @babel/core @babel/preset-env --dev
babel 只是一个转换平台,提供环境,具体起到转换作用的是 babel 内部的插件,preset 是插件集合。
preset-env 就是最新的所有特性的整体打包,使用它可以把所有特性都做转换。
const { src, dest } = require('gulp');
const sass = require('gulp-sass');
const babel = require('gulp-babel');
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'));
}
module.exports = {
style,
script
}
yarn gulp script
页面模板编译
yarn add gulp-swig --dev
const { src, dest } = require('gulp');
const sass = require('gulp-sass');
const babel = require('gulp-babel');
const swig = require('gulp-swig');
const data = require('./src/data');
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'));
}
const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(swig({ data }))
.pipe(dest('dist'));
}
module.exports = {
style,
script,
page
}
yarn gulp page
组合任务
const { src, dest, parallel } = require('gulp');
const sass = require('gulp-sass');
const babel = require('gulp-babel');
const swig = require('gulp-swig');
const data = require('./src/data');
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'));
}
const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(swig({ data }))
.pipe(dest('dist'));
}
const compile = parallel(style, script, page);
module.exports = {
compile
};
yarn gulp compile
图片和字体文件转换
yarn add gulp-imagemin --dev
const { src, dest, parallel } = require('gulp');
const sass = require('gulp-sass');
const babel = require('gulp-babel');
const swig = require('gulp-swig');
const imagemin = require('gulp-imagemin');
const data = require('./src/data');
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'));
}
const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(swig({ data }))
.pipe(dest('dist'));
}
const image = () => {
return src('src/assets/images/**', { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))
}
const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))
}
const compile = parallel(style, script, page, image, font);
module.exports = {
compile
};
yarn gulp compile
其他文件及文件清除
yarn add del --dev
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');
const data = require('./src/data');
const clean = () => {
return del(['dist']);
}
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'));
}
const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(swig({ data }))
.pipe(dest('dist'));
}
const image = () => {
return src('src/assets/images/**', { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))
}
const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
.pipe(imagemin())
.pipe(dest('dist'))
}
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
};
自动加载插件
yarn add gulp-load-plugins --dev
const { src, dest, parallel, series } = require('gulp');
const del = require('del');
const loadPlugins = require('gulp-load-plugins');
const data = require('./src/data');
const plugins = loadPlugins();
const clean = () => {
return del(['dist']);
}
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'));
}
const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(plugins.swig({ data }))
.pipe(dest('dist'));
}
const image = () => {
return src('src/assets/images/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
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
};
开发服务器
热更新开发服务器。
yarn add browser-sync --dev
const { src, dest, parallel, series } = require('gulp');
const del = require('del');
const browserSync = require('browser-sync');
const loadPlugins = require('gulp-load-plugins');
const data = require('./src/data');
const plugins = loadPlugins();
const bs = browserSync.create();
const clean = () => {
return del(['dist']);
}
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'))
}
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'));
}
const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(plugins.swig({ data }))
.pipe(dest('dist'));
}
const image = () => {
return src('src/assets/images/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'))
}
const extra = () => {
return src('public/**', { base: 'public' })
.pipe(dest('dist'));
}
const serve = () => {
bs.init({
notify: false,
port: 3000,
open: false,
files: 'dist/**',
server: {
baseDir: 'dist',
routes: {
'/node_modules': 'node_modules'
}
}
});
}
const compile = parallel(style, script, page, image, font);
const build = series(clean, parallel(compile, extra));
module.exports = {
build,
serve
};
监听变化以及构建变化
使用 gulp 的 watch 方法,配合 browserSync 的 files 属性。
const { src, dest, parallel, series, watch } = require('gulp');
const del = require('del');
const browserSync = require('browser-sync');
const loadPlugins = require('gulp-load-plugins');
const data = require('./src/data');
const plugins = loadPlugins();
const bs = browserSync.create();
const clean = () => {
return del(['dist']);
}
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('dist'));
}
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('dist'));
}
const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(plugins.swig({ data, defaults: { cache: false } }))
.pipe(dest('dist'));
}
const image = () => {
return src('src/assets/images/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'));
}
const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'));
}
const extra = () => {
return src('public/**', { base: 'public' })
.pipe(dest('dist'));
}
const serve = () => {
watch('src/assets/styles/*.scss', style);
watch('src/assets/scripts/*.js', script);
watch('src/*.html', page);
watch([
'src/assets/images/**',
'src/assets/fonts/**',
'public/**'
], bs.reload);
bs.init({
notify: false,
port: 3000,
// open: false,
files: 'dist/**',
server: {
baseDir: [
'dist',
'src',
'public'
],
routes: {
'/node_modules': 'node_modules'
}
}
});
}
const compile = parallel(style, script, page);
const build = series(clean, parallel(compile, extra, image, font));
const dev = series(compile, serve);
module.exports = {
build,
dev
};
开发环境
yarn gulp dev
生产环境
yarn gulp build
还有一种不使用 files 的方法,直接构建过程中追加 bs.reload 方法。
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('temp'))
.pipe(bs.reload({ stream: true }))
}
const serve = () => {
watch('src/assets/styles/*.scss', style)
watch('src/assets/scripts/*.js', script)
watch('src/*.html', page)
watch([
'src/assets/images/**',
'src/assets/fonts/**',
'public/**'
], bs.reload)
bs.init({
notify: 3000,
port: 2080,
server: {
baseDir: ['temp', 'src', 'public'],
routes: {
'/node_modules': 'node_modules'
}
}
})
}
useref 文件引用处理
yarn add gulp-useref
const useref = () => {
return src('dist/*.html', { base: 'dist' })
.pipe(plugins.useref({ searchPath: ['dist', '.'] }))
.pipe(dest('dist'));
}
module.exports = {
useref
};
文件压缩
yarn add gulp-htmlmin gulp-uglify gulp-clean-css --dev
yarn add gulp-if --dev
htmlmin 还有其他配置,比如移除注释、删除空属性
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('temp'));
}
module.exports = {
useref
};
重新规划构建过程
const { src, dest, parallel, series, watch } = require('gulp');
const del = require('del');
const browserSync = require('browser-sync');
const loadPlugins = require('gulp-load-plugins');
const data = require('./src/data');
const plugins = loadPlugins();
const bs = browserSync.create();
const clean = () => {
return del(['dist']);
}
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('temp'));
}
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('temp'));
}
const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(plugins.swig({ data, defaults: { cache: false } }))
.pipe(dest('temp'));
}
const image = () => {
return src('src/assets/images/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'));
}
const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'));
}
const extra = () => {
return src('public/**', { base: 'public' })
.pipe(dest('dist'));
}
const serve = () => {
watch('src/assets/styles/*.scss', style);
watch('src/assets/scripts/*.js', script);
watch('src/*.html', page);
watch([
'src/assets/images/**',
'src/assets/fonts/**',
'public/**'
], bs.reload);
bs.init({
notify: false,
port: 3000,
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('dist'));
}
const compile = parallel(style, script, page);
const build = series(
clean,
parallel(
series(compile, useref),
extra,
image,
font
)
);
const dev = series(compile, serve);
module.exports = {
build,
dev
};
规整构建过程
gulpfile.js
const { src, dest, parallel, series, watch } = require('gulp');
const del = require('del');
const browserSync = require('browser-sync');
const loadPlugins = require('gulp-load-plugins');
const data = require('./src/data');
const plugins = loadPlugins();
const bs = browserSync.create();
const clean = () => {
return del(['dist', 'temp']);
}
const style = () => {
return src('src/assets/styles/*.scss', { base: 'src' })
.pipe(plugins.sass({ outputStyle: 'expanded' }))
.pipe(dest('temp'));
}
const script = () => {
return src('src/assets/scripts/*.js', { base: 'src' })
.pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
.pipe(dest('temp'));
}
const page = () => {
return src('src/*.html', { base: 'src' })
.pipe(plugins.swig({ data, defaults: { cache: false } }))
.pipe(dest('temp'));
}
const image = () => {
return src('src/assets/images/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'));
}
const font = () => {
return src('src/assets/fonts/**', { base: 'src' })
.pipe(plugins.imagemin())
.pipe(dest('dist'));
}
const extra = () => {
return src('public/**', { base: 'public' })
.pipe(dest('dist'));
}
const serve = () => {
watch('src/assets/styles/*.scss', style);
watch('src/assets/scripts/*.js', script);
watch('src/*.html', page);
watch([
'src/assets/images/**',
'src/assets/fonts/**',
'public/**'
], bs.reload);
bs.init({
notify: false,
port: 3000,
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('dist'));
}
const compile = parallel(style, script, page);
const build = series(
clean,
parallel(
series(compile, useref),
extra,
image,
font
)
);
const develop = series(compile, serve);
module.exports = {
clean,
build,
develop
};
package.json
"scripts": {
"clean": "gulp clean",
"build": "gulp build",
"dev": "gulp develop"
}
