《二十九》自动化构建工具 Gulp

基于 Gulp4。

Gulp:是一个工具包,可以帮助自动化和增强工作流。
请添加图片描述

Gulp 和 Webpack 的区别:

  1. Gulp 的核心理念是 task runner。可以定义一系列任务,等到任务被执行;是基于文件Stream 的构建流;可以使用 Gulp 的插件体系来完成任务。
  2. Webpack 的核心理念是 module bundler。是一个模块化的打包工具;可以使用各种各样的 Lodaer 来加载不同的模块;可以使用各种各样的插件在打包的生命周期中完成其他的任务。

Gulp 相对于 Webpack 来说更加地简单易用,适合编写一些自动化的任务,让它们挨个自动去执行,但是对于目前大型的项目(React、Vue、Angular 等)并不会使用 Gulp 来构建。

Gulp 默认不支持模块化。

Gulp 的基本使用:

每个 Gulp 任务都是一个异步的 JavaScript 函数;接收一个回调函数作为参数;必须明确地表示被执行完成,可以通过两种方式:执行作为参数的回调函数,或者返回一个 stream、promise、event emitterchild process 或者 observable 类型的函数。

Gulp 中任务可以分为公开任务和私有任务。

  1. 公开任务:在 gulpfile.js 文件中导出的任务被称为公开任务,这些任务可以通过 gulp 命令直接调用。
  2. 私有任务:被设计为在内部使用,通常作为 series() 或者 parallel() 任务组合的组成部分。

Gulp 单个任务:

  1. 新建 gulp-demo 文件夹,并 npm init -y 初始化该项目。

  2. 在该文件夹下本地安装 Gulp:npm install gulp

  3. 在项目根目录下新建 gulpfile.js 文件,并定义任务。

    // gulpfile.js
    // 定义任务
    const test = callback => {
      console.log('test 任务被执行')
      callback()
    }
    
    // 导出任务
    module.exports = {
      test
    }
    

    在 Gulp4 之前,定义任务的写法为:

    // gulpfile.js
    const gulp = require("gulp")
    gulp.task('test', callback => {
     console.log('test 任务被执行')
     callback()
    })
    
  4. 运行 npx gulp test 命令,会自动去 fulpfile.js 文件中查找对应的任务并执行。

    在这里插入图片描述

默认任务:

如果觉得觉得在 npx gulp 命令还得跟着任务名比较麻烦,可以导出一个 default 任务。直接运行 npx gulp 命令将会自动执行该 default 任务。

// gulpfile.js
// 定义并导出默认任务
module.exports.default = callback => {
  console.log('default 任务被执行')
  callback()
}

在这里插入图片描述

Gulp 多个任务:

Gulp 提供了两个组合方法,它们都可以接收任意数量的任务函数或者已经组合的操作。

  1. series():串行任务组合。多个任务会挨个执行,只有执行完前一个才能执行后一个。
  2. parallel():并行任务组合。多个任务或同时执行,顺序无法确定。
const {parallel} = require('gulp')

// 定义任务
const task1 = callback => {
  setTimeout(() => {
    console.log('task1 任务被执行')
    callback()
  }, 2000)
}
const task2 = callback => {
  setTimeout(() => {
    console.log('task2 任务被执行')
    callback()
  }, 1000)
}
const parallelTask = parallel(task1, task2)

// 导出任务
module.exports = {
  parallelTask
}

在这里插入图片描述

Gulp 读取和写入文件:

Gulp 暴露了 src()dest() 方法用于处理计算机上存放的文件。

  1. src():接收一个 glob 字符串或由多个 glob 字符串组成的数组作为参数,将所有从文件系统中匹配到的文件读取到内存中,并产生一个 Stream 流,通过该 Stream 流进行处理。src() 产生的 Stream 流应当从任务函数中返回,发生异步任务完成的信号。
    glob 的匹配规则:

    • *:在一个字符串中,匹配任意数量的字符。例如 src/*.js
    • **:在多个字符串匹配中,匹配任意数量的字符。通常用在匹配某个目录下的文件。例如 src/**/*.js
    • !:glob 匹配时是按照在数组中的位置依次进行匹配的,glob 数组中的取反 glob 必须跟在一个非取反 glob 后面,表示后面取反 glob 删除掉前面非取反 glob 匹配到的匹配项中的一部分。例如 ['src/**/*.js', 'src/vendor/']

    glob 字符串或者 glob 数组至少需要匹配到一个匹配项,否则 src 将会报错。

  2. dest():接收一个输出目录作为参数,会产生一个 Node Stream 流,通过该 Stream 流将内容输出到文件中。

Stream 流提供的主要的 API 就是 pipe() 方法,pipe() 方法接收一个转换流或者可写流。

  1. 新建 src/index.js 文件,并编写代码。
    const fn = () => {
      console.log('index')
    }
    fn()
    
  2. gulpfile.js 中编写 Gulp 读取和写入文件的方法。
    const {src, dest} = require('gulp')
    
    const output = () => {
      // 读取 src 中的所有内容并输出到 dist 目录下
      return src('./src/*.js').pipe(dest('./dist'))
    }
    
    module.exports = {
      output
    }
    
  3. 运行 npx gulp output 命令,会发现,生成了 dist 目录,并复制了 src 目录下的文件及其内容。
    在这里插入图片描述

Gulp 中的插件:

gulp-babel 插件和 gulp-terser 插件为例,可以对 JavaScript 代码进行转换。

  1. 安装 gulp-babelnpm install gulp-babel -D
  2. 安装 Babel 的核心代码:npm install @babel/core -D
  3. 安装 Babel 中的预设:npm install @babel/preset-env -D
  4. 安装 gulp-tersernpm install gulp-terser -D
  5. gulpfile.js 文件中编写代码使用插件。
    const {src, dest} = require('gulp')
    const babel = require('gulp-babel')
    const terser = require('gulp-terser')
    
    const output = () => {
      // 读取 src 中的所有内容
      return src('./src/*.js')
        // 使用 babel 进行转换
        .pipe(babel({
          presets: ['@babel/preset-env'],
        }))
        // 对其进行压缩和丑化
        .pipe(terser())
        // 输出到 dist 目录下
        .pipe(dest('./dist'))
    }
    
    module.exports = {
      output
    }
    
  6. 运行 npx gulp output 命令,会发现,生成了 dist 目录,复制了 src 目录下的文件及其内容,并对其进行了压缩和丑化。
    在这里插入图片描述

Gulp 的文件监听:

可以使用 Gulp 提供的 watch() 函数监听源文件的变化来执行指定的任务。

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

const output = () => {
  return src('./src/*.js').pipe(dest('./dist'))
}

// 监听 src 中所有内容的变化,只要发生变化就执行 output 任务
watch('./src/*.js', output)

module.exports = {
  output
}

请添加图片描述

Gulp 案例:

  1. 新建 src/index.html 文件,并编写代码。
    // src/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    <body>
    </body>
    </html>
    
  2. 安装 gulp-htmlmin 插件处理 HTML 文件:npm install gulp-htmlmin -D
  3. 新建 src/css/index.less 文件,并编写代码。
    @mainSize: 24px;
    body{
      size: @mainSize;
      user-select: none;
    }
    
  4. 安装 gulp-less 插件处理 Less 文件:npm install gulp-less -D

    安装 gulp-less 插件会自动安装 Less。

  5. 安装 gulp-postcss 自动给 CSS 属性添加浏览器前缀:npm install gulp-postcss -D
  6. 安装 PostCSS:npm install postcss -D
  7. 安装 postcss-preset-env 插件:npm install postcss-preset-env -D
  8. 新建 src/js/index.js 文件,并编写代码。
    // src/js/index.js
    const fn = () => {
      console.log('index111')
    }
    fn()
    
  9. 安装 gulp-babel 插件转换 JavaScript 文件:npm install gulp-babel -D
  10. 安装 Babel 的核心代码:npm install @babel/core -D
  11. 安装 Babel 中的预设:npm install @babel/preset-env -D
  12. 安装 gulp-terser 插件压缩丑化 JavaScript 文件:npm install gulp-terser -D
  13. 安装 gulp-inject 插件将打包后的 CSS、JavaScript 文件注入到 HTML 文件中:npm install gulp-inject -D
  14. src/index.html 中编写要注入文件的魔法注释。
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <!-- inject:css -->
      <!-- endinject -->
    </head>
    <body>
      <!-- inject:js -->
      <!-- endinject -->
    </body>
    </html>
    
  15. 安装 del 插件删除指定的文件夹:npm install del -D
  16. 安装 browser-sync 插件开启一个本地服务:npm install browser-sync -D
  17. 新建 gulpfile.js 文件,并编写任务。
    const {src, dest, watch, series, parallel} = require('gulp')
    const htmlMin = require('gulp-htmlmin')
    const less  = require('gulp-less')
    const postcss = require('gulp-postcss')
    const postcssPresetEnv = require('postcss-preset-env')
    const babel = require('gulp-babel')
    const terser = require('gulp-terser')
    const inject = require('gulp-inject')
    const del = require('del')
    const browseSync = require('browser-sync')
    
    // 处理 HTML 文件的任务
    const htmlTask = () => {
      return src('./src/*.html', {base: './src'})// base:基础文件夹,在输出文件夹下会根据此文件夹下的目录生成同样对应的目录
        // 对 HTML 文件进行压缩
        .pipe(htmlMin({collapseWhitespace: true}))
        .pipe(dest('./dist'))
    }
    
    // 处理 Less 文件的任务
    const lessTask = () => {
      return src('./src/css/*.less', {base: './src'})
        // 对 HTML 文件进行压缩
        .pipe(less())
        .pipe(postcss([postcssPresetEnv()]))
        .pipe(dest('./dist'))
    }
    
    // 处理 JavaScript 文件的任务
    const jsTask = () => {
      return src('./src/js/*.js', {base: './src'})
        // 对 JS 文件进行转换
        .pipe(babel({presets: ['@babel/preset-env']}))
        // 对 JS 文件进行压缩和丑化
        .pipe(terser({mangle: {toplevel: true}}))
        .pipe(dest('./dist'))
    }
    
    // 将打包后的 CSS、JavaScript 文件注入到 HTML 文件中
    const injectHtml = () => {
      return src('./dist/*.html')
        .pipe(inject(src(['./dist/css/*.css', './dist/js/*.js']), {relative: true})) // relative 属性用来配置注入后使用相对路径
        .pipe(dest('./dist'))
    }
    
    // 删除指定的文件
    const cleanTask = () => {
      return del(['dist'])
    }
    
    // 开启一个本地服务
    const bs = browseSync.create() // 创建本地服务器对象
    const serveTask = () => {
      // 监听 src 中的文件发生变化自动重新执行任务进行打包和注入
      watch('./src/*.html', series(htmlTask, injectHtml))
      watch('./src/css/*.less', series(lessTask, injectHtml))
      watch('./src/js/*.js', series(jsTask, injectHtml))
    
      bs.init({
        port: 8080,
        // 自动打开浏览器
        open: true,
        // dist 中的文件发生变化自动刷新浏览器
        files: './dist/*',
        server: {
          // 服务器服务于哪个文件夹
          baseDir: './dist',
        }
      })
    }
    
    // 生产阶段打包
    const build = series(cleanTask, parallel(htmlTask, lessTask, jsTask), injectHtml)
    // 开发阶段打包并开启本地服务
    const serve = series(build, serveTask)
    
    module.exports = {
      build,
      serve
    }
    
  18. 运行 npx gulp serve,会发现, HTML、CSS、JavaScript 文件都被打包到了 dist 文件夹下,并且 CSS、JavaScript 注入到了 HTML 文件中,而且开启了一个本地父亲。
    请添加图片描述