样式编译

  1. const { src, dest } = require('gulp')
  2. const sass = require('gulp-sass')
  3. const style = () => {
  4. return src('src/assets/styles/*.scss', { base: 'src' })
  5. .pipe(sass({ outputStyle: 'expanded' }))
  6. .pipe(dest('dist'))
  7. }
  8. module.exports = {
  9. style
  10. }

脚本编译

  1. const { src, dest } = require('gulp')
  2. const babel = require('gulp-babel')
  3. const script = () => {
  4. return src('src/assets/scripts/*.js', { base: 'src' })
  5. .pipe(babel({ presets: ['@babel/preset-env'] }))
  6. .pipe(dest('dist'))
  7. }
  8. module.exports = {
  9. script
  10. }

页面编译

  1. const { src, dest } = require('gulp')
  2. const swig = require('gulp-swig')
  3. const data = {
  4. menus: [
  5. {
  6. name: 'Home',
  7. icon: 'aperture',
  8. link: 'index.html'
  9. },
  10. {
  11. name: 'Features',
  12. link: 'features.html'
  13. },
  14. {
  15. name: 'About',
  16. link: 'about.html'
  17. },
  18. {
  19. name: 'Contact',
  20. link: '#',
  21. children: [
  22. {
  23. name: 'Twitter',
  24. link: 'https://twitter.com/w_zce'
  25. },
  26. {
  27. name: 'About',
  28. link: 'https://weibo.com/zceme'
  29. },
  30. {
  31. name: 'divider'
  32. },
  33. {
  34. name: 'About',
  35. link: 'https://github.com/zce'
  36. }
  37. ]
  38. }
  39. ],
  40. pkg: require('./package.json'),
  41. date: new Date()
  42. }
  43. const page = () => {
  44. return src('src/*.html', { base: 'src' }) // 遍历子目录下的html文件
  45. .pipe(swig({ data, defaults: { cache: false } }))
  46. .pipe(dest('dist'))
  47. }
  48. module.exports = {
  49. page
  50. }

图片和字体编译

  1. const { src, dest, parallel } = require('gulp')
  2. const imagemin = require('gulp-imagemin')
  3. const image = () => {
  4. return src('src/assets/images/**', { base: 'src' })
  5. .pipe(imagemin())
  6. .pipe(dest('dist'))
  7. }
  8. const font = () => {
  9. return src('src/assets/fonts/**', { base: 'src' })
  10. .pipe(imagemin())
  11. .pipe(dest('dist'))
  12. }
  13. module.exports = {
  14. image,
  15. font
  16. }

其他文件及文件清除

  1. // 其他文件直接复制
  2. const extra = () => {
  3. return src('public/**', { base: 'public' })
  4. .pipe(dest('dist'))
  5. }
  6. // 文件清除
  7. const del = require('del')
  8. const clean = () => {
  9. return del(['dist'])
  10. }

任务组合

  1. const { src, dest, parallel, series } = require('gulp')
  2. const compile = parallel(style, script, page, image, font)
  3. const build = series(clean, parallel(compile, extra))
  4. module.exports = {
  5. build
  6. }

自动加载插件

  1. const loadPlugins = require('gulp-load-plugins')
  2. const plugins = loadPlugins()
  3. // 改写 style 任务
  4. const style = () => {
  5. return src('src/assets/styles/*.scss', { base: 'src' })
  6. .pipe(plugins.sass({ outputStyle: 'expanded' }))
  7. .pipe(dest('dist'))
  8. }
  9. module.exports = {
  10. style
  11. }

开发服务器

  1. const browserSync = require('browser-sync')
  2. const bs = browserSync.create()
  3. const serve = () => {
  4. bs.init({
  5. notify: false, // 是否在浏览器窗口弹出提示信息, 默认 true
  6. files: 'dist/**', // 监视文件变化时自动刷新
  7. port: 3080, // 端口,默认
  8. open: false, // 是否自动打开浏览器,默认true
  9. server: {
  10. baseDir: 'dist', // 基本路径
  11. routes: {
  12. '/node_modules': 'node_modules' // 特殊路由,优先于baseDir的配置
  13. }
  14. }
  15. })
  16. }
  17. module.exports = {
  18. serve
  19. }

监视变化及构建优化

在开发阶段,字体、图片的编译是不必要的,会拖慢应用启用的速度,应该进行优化。

  1. const { src, dest, parallel, series, watch } = require('gulp')
  2. const del = require('del')
  3. const browserSync = require('browser-sync')
  4. const loadPlugins = require('gulp-load-plugins')
  5. const plugins = loadPlugins()
  6. const bs = browserSync.create()
  7. const data = {
  8. menus: [
  9. {
  10. name: 'Home',
  11. icon: 'aperture',
  12. link: 'index.html'
  13. },
  14. {
  15. name: 'Features',
  16. link: 'features.html'
  17. },
  18. {
  19. name: 'About',
  20. link: 'about.html'
  21. },
  22. {
  23. name: 'Contact',
  24. link: '#',
  25. children: [
  26. {
  27. name: 'Twitter',
  28. link: 'https://twitter.com/w_zce'
  29. },
  30. {
  31. name: 'About',
  32. link: 'https://weibo.com/zceme'
  33. },
  34. {
  35. name: 'divider'
  36. },
  37. {
  38. name: 'About',
  39. link: 'https://github.com/zce'
  40. }
  41. ]
  42. }
  43. ],
  44. pkg: require('./package.json'),
  45. date: new Date()
  46. }
  47. const clean = () => {
  48. return del(['dist'])
  49. }
  50. const style = () => {
  51. return src('src/assets/styles/*.scss', { base: 'src' })
  52. .pipe(plugins.sass({ outputStyle: 'expanded' }))
  53. .pipe(dest('dist'))
  54. .pipe(bs.reload({ stream: true })) // 如果 应用服务器没有配置 files 项,可以通过这样的方式自动刷新浏览器
  55. }
  56. const script = () => {
  57. return src('src/assets/scripts/*.js', { base: 'src' })
  58. .pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
  59. .pipe(dest('dist'))
  60. .pipe(bs.reload({ stream: true }))
  61. }
  62. const page = () => {
  63. return src('src/*.html', { base: 'src' })
  64. .pipe(plugins.swig({ data, defaults: { cache: false } }))
  65. .pipe(dest('dist'))
  66. .pipe(bs.reload({ stream: true }))
  67. }
  68. const image = () => {
  69. return src('src/assets/images/**', { base: 'src' })
  70. .pipe(plugins.imagemin())
  71. .pipe(dest('dist'))
  72. }
  73. const font = () => {
  74. return src('src/assets/fonts/**', { base: 'src' })
  75. .pipe(plugins.imagemin())
  76. .pipe(dest('dist'))
  77. }
  78. const extra = () => {
  79. return src('public/**', { base: 'public' })
  80. .pipe(dest('dist'))
  81. }
  82. const serve = () => {
  83. watch('src/assets/styles/*.scss', style)
  84. watch('src/assets/scripts/*.js', script)
  85. watch('src/*.html', page)
  86. watch([
  87. 'src/assets/images/**',
  88. 'src/assets/fonts/**',
  89. 'public/**',
  90. ], bs.reload) // 当图片、字体有变化,不编译直接刷新浏览器
  91. // watch('src/assets/images/**', image)
  92. // watch('src/assets/fonts/**', font)
  93. // watch('public/**', extra)
  94. bs.init({
  95. notify: false,
  96. // files: 'dist/**',
  97. port: 3080,
  98. // open: false,
  99. server: {
  100. baseDir: ['dist', 'src', 'public'],
  101. routes: {
  102. '/node_modules': 'node_modules'
  103. }
  104. }
  105. })
  106. }
  107. const compile = parallel(style, script, page)
  108. const build = series(clean, parallel(compile, image, font, extra))
  109. const develop = series(compile, serve)
  110. module.exports = {
  111. develop,
  112. build,
  113. }

useref 文件引用处理

useref 插件可以在编译时处理文件中对 js 和 css 的引用路径,但是需要一定格式的注释

  1. <!-- build:css assets/styles/vendor.css -->
  2. <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
  3. <!-- endbuild -->
  4. <!-- build:css assets/styles/main.css -->
  5. <link rel="stylesheet" href="assets/styles/main.css">
  6. <!-- endbuild -->
  7. <!-- build:js assets/scripts/vendor.js -->
  8. <script src="/node_modules/jquery/dist/jquery.js"></script>
  9. <script src="/node_modules/popper.js/dist/umd/popper.js"></script>
  10. <script src="/node_modules/bootstrap/dist/js/bootstrap.js"></script>
  11. <!-- endbuild -->
  12. <!-- build:js assets/scripts/main.js -->
  13. <script src="assets/scripts/main.js"></script>
  14. <!-- endbuild -->

入口文件配置:

  1. const useref = () => {
  2. return src('dist/*.html', { base: 'dist' })
  3. .pipe(plugins.useref({ searchPath: ['dist', '.' ] }))
  4. .pipe(dest('dist'))
  5. }
  6. module.exports = {
  7. useref
  8. }
  1. 结果:
  1. <link rel="stylesheet" href="assets/styles/vendor.css">
  2. <link rel="stylesheet" href="assets/styles/main.css">
  3. <script src="assets/scripts/vendor.js"></script>
  4. <script src="assets/scripts/main.js"></script>

文件压缩

  1. const useref = () => {
  2. return src('dist/*.html', { base: 'dist' })
  3. .pipe(plugins.useref({ searchPath: ['dist', '.'] }))
  4. .pipe(plugins.if(/\.js$/, plugins.uglify()))
  5. .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
  6. .pipe(plugins.if(/\.html$/, plugins.htmlmin({
  7. collapseWhitespace: true,
  8. minifyCSS: true,
  9. minifyJS: true
  10. })))
  11. .pipe(dest('release')) // 这里不能输出到 dist 文件夹,不然会造成读写冲突
  12. }

重新规划构建过程

为了保持之前的构建过程,避免文件读写冲突。应该先把需要编译的样式、页面、脚本文件输出到一个临时目录,在执行 useref 任务后再输出到 dist 目录。最终的入口文件如下:

  1. const { src, dest, parallel, series, watch } = require('gulp')
  2. const del = require('del')
  3. const browserSync = require('browser-sync')
  4. const loadPlugins = require('gulp-load-plugins')
  5. const plugins = loadPlugins()
  6. const bs = browserSync.create()
  7. const data = {
  8. menus: [
  9. {
  10. name: 'Home',
  11. icon: 'aperture',
  12. link: 'index.html'
  13. },
  14. {
  15. name: 'Features',
  16. link: 'features.html'
  17. },
  18. {
  19. name: 'About',
  20. link: 'about.html'
  21. },
  22. {
  23. name: 'Contact',
  24. link: '#',
  25. children: [
  26. {
  27. name: 'Twitter',
  28. link: 'https://twitter.com/w_zce'
  29. },
  30. {
  31. name: 'About',
  32. link: 'https://weibo.com/zceme'
  33. },
  34. {
  35. name: 'divider'
  36. },
  37. {
  38. name: 'About',
  39. link: 'https://github.com/zce'
  40. }
  41. ]
  42. }
  43. ],
  44. pkg: require('./package.json'),
  45. date: new Date()
  46. }
  47. const clean = () => {
  48. return del(['dist', 'temp'])
  49. }
  50. const style = () => {
  51. return src('src/assets/styles/*.scss', { base: 'src' })
  52. .pipe(plugins.sass({ outputStyle: 'expanded' }))
  53. .pipe(dest('temp'))
  54. .pipe(bs.reload({ stream: true }))
  55. }
  56. const script = () => {
  57. return src('src/assets/scripts/*.js', { base: 'src' })
  58. .pipe(plugins.babel({ presets: ['@babel/preset-env'] }))
  59. .pipe(dest('temp'))
  60. .pipe(bs.reload({ stream: true }))
  61. }
  62. const page = () => {
  63. return src('src/*.html', { base: 'src' })
  64. .pipe(plugins.swig({ data, defaults: { cache: false } }))
  65. .pipe(dest('temp'))
  66. .pipe(bs.reload({ stream: true }))
  67. }
  68. const image = () => {
  69. return src('src/assets/images/**', { base: 'src' })
  70. .pipe(plugins.imagemin())
  71. .pipe(dest('dist'))
  72. }
  73. const font = () => {
  74. return src('src/assets/fonts/**', { base: 'src' })
  75. .pipe(plugins.imagemin())
  76. .pipe(dest('dist'))
  77. }
  78. const extra = () => {
  79. return src('public/**', { base: 'public' })
  80. .pipe(dest('dist'))
  81. }
  82. const serve = () => {
  83. watch('src/assets/styles/*.scss', style)
  84. watch('src/assets/scripts/*.js', script)
  85. watch('src/*.html', page)
  86. watch([
  87. 'src/assets/images/**',
  88. 'src/assets/fonts/**',
  89. 'public/**',
  90. ], bs.reload)
  91. // watch('src/assets/images/**', image)
  92. // watch('src/assets/fonts/**', font)
  93. // watch('public/**', extra)
  94. bs.init({
  95. notify: false,
  96. // files: 'dist/**',
  97. port: 3080,
  98. // open: false,
  99. server: {
  100. baseDir: ['temp', 'src', 'public'],
  101. routes: {
  102. '/node_modules': 'node_modules'
  103. }
  104. }
  105. })
  106. }
  107. const useref = () => {
  108. return src('temp/*.html', { base: 'temp' })
  109. .pipe(plugins.useref({ searchPath: ['temp', '.'] }))
  110. .pipe(plugins.if(/\.js$/, plugins.uglify()))
  111. .pipe(plugins.if(/\.css$/, plugins.cleanCss()))
  112. .pipe(plugins.if(/\.html$/, plugins.htmlmin({
  113. collapseWhitespace: true,
  114. minifyCSS: true,
  115. minifyJS: true
  116. })))
  117. .pipe(dest('dist'))
  118. }
  119. const compile = parallel(style, script, page)
  120. const build = series(clean, parallel(series(compile, useref), image, font, extra))
  121. const develop = series(compile, serve)
  122. module.exports = {
  123. clean,
  124. develop,
  125. build,
  126. }

package.json 文件可以配置脚本命令。

  1. "scripts": {
  2. "clean": "gulp clean",
  3. "build": "gulp build",
  4. "develop": "gulp develop"
  5. },