Skip to content

Gulp 介绍

什么是 Gulp

Gulp 是一个基于流的自动化构建工具。它使用 Node.js 流的力量,将文件视为流,通过管道(pipe)将多个任务串联起来,实现自动化构建流程。Gulp 强调"代码优于配置",通过编写代码来定义任务。

Gulp 的核心特点

基于流

  • 使用 Node.js 流处理文件
  • 内存中操作,效率高
  • 文件按顺序通过管道处理

代码优于配置

  • 使用 JavaScript 编写任务
  • 灵活性高
  • 可编程控制流程

任务化

  • 将构建过程分解为小任务
  • 任务可以组合和并行
  • 清晰的依赖关系

插件丰富

  • 大量社区插件
  • 功能覆盖全面
  • 易于扩展

Gulp 的工作原理

流的概念

Gulp 使用 Node.js 的流(Stream)来处理文件:

源文件(Stream)
    ↓ pipe()
转换插件
    ↓ pipe()
转换插件
    ↓ pipe()
目标文件夹

Vinyl 文件对象

Gulp 使用 vinyl-fs 模块,将文件转换为 Vinyl 对象:

javascript
{
  cwd: '/project/',
  base: '/project/src/',
  path: '/project/src/file.js',
  contents: <Stream | Buffer | null>
}

任务执行

Gulp 4.0 使用 task 组合 API:

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

// 串行执行
exports.build = series(taskA, taskB);

// 并行执行
exports.build = parallel(taskA, taskB);

// 组合使用
exports.build = series(clean, parallel(css, js), html);

Gulp 的安装与配置

安装

bash
# 全局安装 CLI
npm install --global gulp-cli

# 本地安装 gulp
npm install --save-dev gulp

创建 gulpfile.js

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

function html() {
  return src('src/*.html')
    .pipe(dest('dist'));
}

function css() {
  return src('src/css/*.css')
    .pipe(dest('dist/css'));
}

function js() {
  return src('src/js/*.js')
    .pipe(dest('dist/js'));
}

exports.build = parallel(html, css, js);
exports.default = series(exports.build);

Gulp API 详解

src()

读取文件创建流:

javascript
src('src/**/*.js')
src(['src/**/*.js', '!src/**/*.min.js'])
src('src/*.js', { 
  base: 'src',      // 基础路径
  cwd: 'project',   // 当前工作目录
  sourcemaps: true  // 启用 sourcemap
})

dest()

写入文件到目录:

javascript
dest('dist')
dest('dist', { 
  mode: 0o755,      // 文件权限
  dirMode: 0o755,   // 目录权限
  overwrite: true   // 覆盖现有文件
})

series()

串行执行任务:

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

exports.build = series(clean, compile, minify);

parallel()

并行执行任务:

javascript
const { parallel } = require('gulp');

exports.build = parallel(html, css, js);

watch()

监听文件变化:

javascript
const { watch, series } = require('gulp');

watch('src/**/*.js', series(jsTask));
watch(['src/**/*.js', 'src/**/*.css'], series(build));

task()

定义任务(旧 API,不推荐):

javascript
const { task } = require('gulp');

task('build', function() {
  return src('src/*.js')
    .pipe(dest('dist'));
});

常用插件

JavaScript 处理

javascript
const { src, dest } = require('gulp');
const babel = require('gulp-babel');
const terser = require('gulp-terser');
const concat = require('gulp-concat');

function js() {
  return src('src/js/*.js')
    .pipe(babel({
      presets: ['@babel/preset-env']
    }))
    .pipe(concat('bundle.js'))
    .pipe(terser())
    .pipe(dest('dist/js'));
}

CSS 处理

javascript
const { src, dest } = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');

function css() {
  return src('src/scss/*.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(postcss([autoprefixer(), cssnano()]))
    .pipe(dest('dist/css'));
}

HTML 处理

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

function html() {
  return src('src/*.html')
    .pipe(htmlmin({ 
      collapseWhitespace: true,
      removeComments: true
    }))
    .pipe(dest('dist'));
}

图片处理

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

function images() {
  return src('src/images/*')
    .pipe(imagemin([
      imagemin.gifsicle({ interlaced: true }),
      imagemin.mozjpeg({ quality: 75, progressive: true }),
      imagemin.optipng({ optimizationLevel: 5 }),
      imagemin.svgo({ plugins: [{ cleanupIDs: false }] })
    ]))
    .pipe(dest('dist/images'));
}

文件操作

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

function cleanTask() {
  return src('dist', { read: false, allowEmpty: true })
    .pipe(clean());
}

function js() {
  return src('src/js/*.js')
    .pipe(sourcemaps.init())
    .pipe(babel())
    .pipe(terser())
    .pipe(rename({ suffix: '.min' }))
    .pipe(sourcemaps.write('.'))
    .pipe(dest('dist/js'));
}

Gulp 的优点

灵活性高

  • 代码优于配置
  • 可以编写任意逻辑
  • 完全控制构建流程

易于理解

  • 管道式处理直观
  • 任务概念清晰
  • 学习曲线平缓

流式处理

  • 内存中操作,效率高
  • 减少临时文件
  • 管道式处理

插件丰富

  • 大量社区插件
  • 功能覆盖全面
  • 易于扩展

与其他工具配合

  • 可以调用其他工具
  • 可以作为任务运行器
  • 适合复杂构建流程

Gulp 的缺点

配置繁琐

  • 需要手动编写任务
  • 复杂项目配置量大
  • 需要了解各种插件

开发体验

  • 没有内置开发服务器
  • HMR 需要额外配置
  • 不如 Webpack/Vite 方便

打包能力

  • 不是专门的打包工具
  • 模块打包需要额外插件
  • 不如 Webpack/Rollup 专业

学习成本

  • 需要学习各种插件
  • 需要了解 Node.js 流
  • 调试相对困难

Gulp 与其他工具对比

特性GulpWebpackVite
类型任务运行器模块打包器构建工具
配置方式代码配置文件配置文件
开发服务器需插件内置内置
HMR需插件内置内置
模块打包需插件原生Rollup
灵活性
学习曲线平缓陡峭平缓

Gulp 的应用场景

任务自动化

  • 文件复制/删除
  • 图片压缩
  • CSS/JS 压缩
  • 文件监听

多语言项目

  • PHP + 前端
  • Java + 前端
  • .NET + 前端

复杂构建流程

  • 多步骤构建
  • 条件构建
  • 自定义流程

与其他工具配合

  • 作为任务调度器
  • 调用其他构建工具
  • CI/CD 流程

完整配置示例

基础项目配置

javascript
const { src, dest, series, parallel, watch } = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const babel = require('gulp-babel');
const terser = require('gulp-terser');
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const sourcemaps = require('gulp-sourcemaps');
const clean = require('gulp-clean');
const browserSync = require('browser-sync').create();

function cleanTask() {
  return src('dist', { read: false, allowEmpty: true })
    .pipe(clean());
}

function html() {
  return src('src/*.html')
    .pipe(dest('dist'))
    .pipe(browserSync.stream());
}

function css() {
  return src('src/scss/*.scss')
    .pipe(sourcemaps.init())
    .pipe(sass().on('error', sass.logError))
    .pipe(postcss([autoprefixer(), cssnano()]))
    .pipe(sourcemaps.write('.'))
    .pipe(dest('dist/css'))
    .pipe(browserSync.stream());
}

function js() {
  return src('src/js/*.js')
    .pipe(sourcemaps.init())
    .pipe(babel({ presets: ['@babel/preset-env'] }))
    .pipe(terser())
    .pipe(sourcemaps.write('.'))
    .pipe(dest('dist/js'))
    .pipe(browserSync.stream());
}

function serve() {
  browserSync.init({
    server: 'dist'
  });
  
  watch('src/*.html', html);
  watch('src/scss/*.scss', css);
  watch('src/js/*.js', js);
}

const build = series(cleanTask, parallel(html, css, js));

exports.clean = cleanTask;
exports.build = build;
exports.serve = series(build, serve);
exports.default = build;

库开发配置

javascript
const { src, dest, series, parallel } = require('gulp');
const babel = require('gulp-babel');
const terser = require('gulp-terser');
const rename = require('gulp-rename');
const typescript = require('gulp-typescript');

const tsProject = typescript.createProject('tsconfig.json');

function compileTs() {
  return src('src/**/*.ts')
    .pipe(tsProject())
    .pipe(dest('dist'));
}

function compileJs() {
  return src('src/**/*.js')
    .pipe(babel({
      presets: ['@babel/preset-env']
    }))
    .pipe(dest('dist'));
}

function minify() {
  return src('dist/**/*.js')
    .pipe(terser())
    .pipe(rename({ suffix: '.min' }))
    .pipe(dest('dist'));
}

exports.build = series(parallel(compileTs, compileJs), minify);

多环境配置

javascript
const { src, dest, series, parallel } = require('gulp');
const gulpIf = require('gulp-if');
const replace = require('gulp-replace');

const isProduction = process.env.NODE_ENV === 'production';

function html() {
  return src('src/*.html')
    .pipe(gulpIf(isProduction, replace('__API_URL__', 'https://api.example.com')))
    .pipe(gulpIf(!isProduction, replace('__API_URL__', 'http://localhost:3000')))
    .pipe(dest('dist'));
}

function js() {
  return src('src/js/*.js')
    .pipe(gulpIf(isProduction, terser()))
    .pipe(dest('dist/js'));
}

exports.build = parallel(html, js);
exports.prod = series(() => { process.env.NODE_ENV = 'production'; }, exports.build);
exports.dev = series(() => { process.env.NODE_ENV = 'development'; }, exports.build);

常用插件列表

插件用途
gulp-babelBabel 编译
gulp-typescriptTypeScript 编译
gulp-sassSass 编译
gulp-lessLess 编译
gulp-postcssPostCSS 处理
gulp-terserJS 压缩
gulp-htmlminHTML 压缩
gulp-imagemin图片压缩
gulp-concat文件合并
gulp-rename文件重命名
gulp-sourcemapsSource Map
gulp-clean清理文件
gulp-if条件判断
gulp-replace文本替换
gulp-plumber错误处理
gulp-zip打包压缩

命令行使用

bash
# 运行默认任务
gulp

# 运行指定任务
gulp build

# 运行多个任务
gulp clean build

# 列出所有任务
gulp --tasks

# 显示任务树
gulp --tasks-depth

# 静默模式
gulp build --silent

# 颜色输出
gulp build --color

# 指定 gulpfile
gulp --gulpfile gulpfile.prod.js

# 指定 cwd
gulp --cwd ./project

最佳实践

错误处理

javascript
const plumber = require('gulp-plumber');
const notify = require('gulp-notify');

function js() {
  return src('src/js/*.js')
    .pipe(plumber({
      errorHandler: notify.onError('Error: <%= error.message %>')
    }))
    .pipe(babel())
    .pipe(dest('dist/js'));
}

任务依赖

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

const clean = () => src('dist', { read: false, allowEmpty: true }).pipe(clean());
const buildCss = () => src('src/css/*.css').pipe(dest('dist/css'));
const buildJs = () => src('src/js/*.js').pipe(dest('dist/js'));
const buildHtml = () => src('src/*.html').pipe(dest('dist'));

exports.build = series(clean, parallel(buildCss, buildJs), buildHtml);

增量构建

javascript
const { src, dest, lastRun } = require('gulp');

function js() {
  return src('src/js/*.js', { since: lastRun(js) })
    .pipe(babel())
    .pipe(dest('dist/js'));
}

基于 VitePress 的本地知识库