Gulp 作为当下最流行的前端构建系统,其核心特点就是高效、易用。

基本使用

  1. yarn init -y
  1. yarn add gulp --dev
  1. code gulpfile.js

Gulp 最新版需要使用 done 标识任务结束。

  1. // gulp 入口文件
  2. exports.foo = done => {
  3. console.log('foo task working');
  4. done(); // 标识任务完成
  5. }
  6. exports.default = done => {
  7. console.log('default task working');
  8. done();
  9. }

gulp 4.0 之前注册 Gulp 任务需要通过 Gulp 模块内部的方法实现。Gulp 4.0 以后保留了这种方式。

  1. const gulp = require('gulp');
  2. gulp.task('bar', done => {
  3. console.log('bar task working');
  4. done();
  5. });

组合任务

  1. const { series, parallel } = require('gulp');
  2. const task1 = done => {
  3. setTimeout(() => {
  4. console.log('task1 working');
  5. done();
  6. }, 1000);
  7. }
  8. const task2 = done => {
  9. setTimeout(() => {
  10. console.log('task2 working');
  11. done();
  12. }, 1000);
  13. }
  14. const task3 = done => {
  15. setTimeout(() => {
  16. console.log('task3 working');
  17. done();
  18. }, 1000);
  19. }
  20. exports.foo = series(task1, task2, task3); // 串行执行
  21. exports.bar = parallel(task1, task2, task3); // 并行执行

构建 JS、CSS 时可以使用 parallel。代码部署可以使用 series,部署前必须构建完毕。

异步任务

回调方式

  1. exports.callback = done => {
  2. console.log('callback task');
  3. done();
  4. }
  5. exports.callback = done => {
  6. console.log('callback task');
  7. done(new Error('task failed'));
  8. }

Promise 方式

  1. exports.promise = () => {
  2. console.log('promise task');
  3. return Promise.resolve();
  4. }
  5. exports.promise = () => {
  6. console.log('promise task');
  7. return Promise.reject(new Error('task failed'));
  8. }

async/await 方式

  1. const timeout = time => {
  2. return new Promise(resolve => {
  3. setTimeout(resolve, time);
  4. });
  5. }
  6. exports.async = async () => {
  7. await timeout(1000);
  8. console.log('async task');
  9. }

stream 方式

  1. exports.stream = () => {
  2. const readStream = fs.createReadStream('package.json');
  3. const wirteStream = fs.createWriteStream('temp.txt');
  4. readStream.pipe(wirteStream);
  5. return readStream;
  6. }

构建过程

输入(读取流) => 加工(转换流) => 输出(写入流)

  1. /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
  2. /* Document
  3. ========================================================================== */
  4. /**
  5. * 1. Correct the line height in all browsers.
  6. * 2. Prevent adjustments of font size after orientation changes in iOS.
  7. */
  8. html {
  9. line-height: 1.15; /* 1 */
  10. -webkit-text-size-adjust: 100%; /* 2 */
  11. }
  12. /* Sections
  13. ========================================================================== */
  14. /**
  15. * Remove the margin in all browsers.
  16. */
  17. body {
  18. margin: 0;
  19. }
  20. /**
  21. * Render the `main` element consistently in IE.
  22. */
  23. main {
  24. display: block;
  25. }
  26. /**
  27. * Correct the font size and margin on `h1` elements within `section` and
  28. * `article` contexts in Chrome, Firefox, and Safari.
  29. */
  30. h1 {
  31. font-size: 2em;
  32. margin: 0.67em 0;
  33. }
  34. /* Grouping content
  35. ========================================================================== */
  36. /**
  37. * 1. Add the correct box sizing in Firefox.
  38. * 2. Show the overflow in Edge and IE.
  39. */
  40. hr {
  41. box-sizing: content-box; /* 1 */
  42. height: 0; /* 1 */
  43. overflow: visible; /* 2 */
  44. }
  45. /**
  46. * 1. Correct the inheritance and scaling of font size in all browsers.
  47. * 2. Correct the odd `em` font sizing in all browsers.
  48. */
  49. pre {
  50. font-family: monospace, monospace; /* 1 */
  51. font-size: 1em; /* 2 */
  52. }
  53. /* Text-level semantics
  54. ========================================================================== */
  55. /**
  56. * Remove the gray background on active links in IE 10.
  57. */
  58. a {
  59. background-color: transparent;
  60. }
  61. /**
  62. * 1. Remove the bottom border in Chrome 57-
  63. * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
  64. */
  65. abbr[title] {
  66. border-bottom: none; /* 1 */
  67. text-decoration: underline; /* 2 */
  68. text-decoration: underline dotted; /* 2 */
  69. }
  70. /**
  71. * Add the correct font weight in Chrome, Edge, and Safari.
  72. */
  73. b,
  74. strong {
  75. font-weight: bolder;
  76. }
  77. /**
  78. * 1. Correct the inheritance and scaling of font size in all browsers.
  79. * 2. Correct the odd `em` font sizing in all browsers.
  80. */
  81. code,
  82. kbd,
  83. samp {
  84. font-family: monospace, monospace; /* 1 */
  85. font-size: 1em; /* 2 */
  86. }
  87. /**
  88. * Add the correct font size in all browsers.
  89. */
  90. small {
  91. font-size: 80%;
  92. }
  93. /**
  94. * Prevent `sub` and `sup` elements from affecting the line height in
  95. * all browsers.
  96. */
  97. sub,
  98. sup {
  99. font-size: 75%;
  100. line-height: 0;
  101. position: relative;
  102. vertical-align: baseline;
  103. }
  104. sub {
  105. bottom: -0.25em;
  106. }
  107. sup {
  108. top: -0.5em;
  109. }
  110. /* Embedded content
  111. ========================================================================== */
  112. /**
  113. * Remove the border on images inside links in IE 10.
  114. */
  115. img {
  116. border-style: none;
  117. }
  118. /* Forms
  119. ========================================================================== */
  120. /**
  121. * 1. Change the font styles in all browsers.
  122. * 2. Remove the margin in Firefox and Safari.
  123. */
  124. button,
  125. input,
  126. optgroup,
  127. select,
  128. textarea {
  129. font-family: inherit; /* 1 */
  130. font-size: 100%; /* 1 */
  131. line-height: 1.15; /* 1 */
  132. margin: 0; /* 2 */
  133. }
  134. /**
  135. * Show the overflow in IE.
  136. * 1. Show the overflow in Edge.
  137. */
  138. button,
  139. input { /* 1 */
  140. overflow: visible;
  141. }
  142. /**
  143. * Remove the inheritance of text transform in Edge, Firefox, and IE.
  144. * 1. Remove the inheritance of text transform in Firefox.
  145. */
  146. button,
  147. select { /* 1 */
  148. text-transform: none;
  149. }
  150. /**
  151. * Correct the inability to style clickable types in iOS and Safari.
  152. */
  153. button,
  154. [type="button"],
  155. [type="reset"],
  156. [type="submit"] {
  157. -webkit-appearance: button;
  158. }
  159. /**
  160. * Remove the inner border and padding in Firefox.
  161. */
  162. button::-moz-focus-inner,
  163. [type="button"]::-moz-focus-inner,
  164. [type="reset"]::-moz-focus-inner,
  165. [type="submit"]::-moz-focus-inner {
  166. border-style: none;
  167. padding: 0;
  168. }
  169. /**
  170. * Restore the focus styles unset by the previous rule.
  171. */
  172. button:-moz-focusring,
  173. [type="button"]:-moz-focusring,
  174. [type="reset"]:-moz-focusring,
  175. [type="submit"]:-moz-focusring {
  176. outline: 1px dotted ButtonText;
  177. }
  178. /**
  179. * Correct the padding in Firefox.
  180. */
  181. fieldset {
  182. padding: 0.35em 0.75em 0.625em;
  183. }
  184. /**
  185. * 1. Correct the text wrapping in Edge and IE.
  186. * 2. Correct the color inheritance from `fieldset` elements in IE.
  187. * 3. Remove the padding so developers are not caught out when they zero out
  188. * `fieldset` elements in all browsers.
  189. */
  190. legend {
  191. box-sizing: border-box; /* 1 */
  192. color: inherit; /* 2 */
  193. display: table; /* 1 */
  194. max-width: 100%; /* 1 */
  195. padding: 0; /* 3 */
  196. white-space: normal; /* 1 */
  197. }
  198. /**
  199. * Add the correct vertical alignment in Chrome, Firefox, and Opera.
  200. */
  201. progress {
  202. vertical-align: baseline;
  203. }
  204. /**
  205. * Remove the default vertical scrollbar in IE 10+.
  206. */
  207. textarea {
  208. overflow: auto;
  209. }
  210. /**
  211. * 1. Add the correct box sizing in IE 10.
  212. * 2. Remove the padding in IE 10.
  213. */
  214. [type="checkbox"],
  215. [type="radio"] {
  216. box-sizing: border-box; /* 1 */
  217. padding: 0; /* 2 */
  218. }
  219. /**
  220. * Correct the cursor style of increment and decrement buttons in Chrome.
  221. */
  222. [type="number"]::-webkit-inner-spin-button,
  223. [type="number"]::-webkit-outer-spin-button {
  224. height: auto;
  225. }
  226. /**
  227. * 1. Correct the odd appearance in Chrome and Safari.
  228. * 2. Correct the outline style in Safari.
  229. */
  230. [type="search"] {
  231. -webkit-appearance: textfield; /* 1 */
  232. outline-offset: -2px; /* 2 */
  233. }
  234. /**
  235. * Remove the inner padding in Chrome and Safari on macOS.
  236. */
  237. [type="search"]::-webkit-search-decoration {
  238. -webkit-appearance: none;
  239. }
  240. /**
  241. * 1. Correct the inability to style clickable types in iOS and Safari.
  242. * 2. Change font properties to `inherit` in Safari.
  243. */
  244. ::-webkit-file-upload-button {
  245. -webkit-appearance: button; /* 1 */
  246. font: inherit; /* 2 */
  247. }
  248. /* Interactive
  249. ========================================================================== */
  250. /*
  251. * Add the correct display in Edge, IE 10+, and Firefox.
  252. */
  253. details {
  254. display: block;
  255. }
  256. /*
  257. * Add the correct display in all browsers.
  258. */
  259. summary {
  260. display: list-item;
  261. }
  262. /* Misc
  263. ========================================================================== */
  264. /**
  265. * Add the correct display in IE 10+.
  266. */
  267. template {
  268. display: none;
  269. }
  270. /**
  271. * Add the correct display in IE 10.
  272. */
  273. [hidden] {
  274. display: none;
  275. }
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"
}