我习惯了用 Vite 和 Webpack 开发 Vue 项目,但最近接手了一个使用 jQuery 的老项目,里面用的是 Gulp 构建工具,这促使我深入学习它。

Gulp 虽然不像 Vite 那样“现代”,却在静态页面、jQuery 项目等非模块化场景中依然高效实用。

自动化构建工具可以帮我们又快又好的完成自动化构建任务

下面是vite、webpack、Gulp的一些区别,利于我们更好的区别和学习

对比维度 Gulp Webpack Vite
工具定位 任务执行器(Task Runner) 模块打包器(Module Bundler) 新一代构建工具,开发服务器 + 打包工具
核心作用 编译 CSS、压缩图片、拷贝文件、自动刷新 分析依赖、打包 JS/CSS/图片等所有资源 极速开发体验(原生 ESM),构建用 Rollup 输出
工作方式 流式处理文件(pipe) 构建模块依赖图,打包成一个/多个 Bundle 开发时不打包(按需加载),构建时用 Rollup
适合项目 静态页面、JQuery 项目、非模块化老项目 Vue/React 项目、模块化中大型项目 Vue/React/TS 等现代前端项目,注重启动快和热更快
打包能力 ❌(不是模块打包器) ✅ 全功能打包 ✅ 开发快、构建快
性能表现 一般(Node.js 处理所有任务) 构建阶段略慢,适合优化 开发阶段极快(esbuild),构建也很快(Rollup)
配置难度 低(但需要你自己写流程) 高(功能多,灵活性强) 极低(开箱即用)
生态插件 多(但老旧,维护不活跃) 非常多 快速增长中,Vue 官方推荐
学习成本 简单 较高(需要理解模块打包机制) 低(默认配置满足大多数需求)

举个简单例子

比如你要完成一个项目:

编译 Sass、压缩 CSS、打包 JS、处理模块依赖、浏览器自动刷新。

当然这三者可以一起使用

如下组合场景很常见:

  • Webpack/Vite 来处理 JS 模块打包
  • Gulp 来处理图片压缩、HTML 压缩、移动文件等额外任务

例如:

npm run build     # 用 Vite 构建打包 Vue 项目
npx gulp deploy   # 用 Gulp 把 dist 拷贝到目标服务器

# 介绍Gulp

然后说回Gulp

Gulp 是一个基于 Node.js 的 自动化任务执行工具(Task Runner),用于处理常见构建任务,如:

  • 编译 Sass/Less
  • 压缩 CSS/JS/图片
  • 浏览器实时刷新
  • 拷贝/重命名文件
  • 清理目录

核心概念:流式处理文件(src → pipe → dest)

# 开始使用

现在让我们来开始使用这个工具吧

# 环境检查和安装

# 检查node、npm、npx

node --version
npm --version
npx --version

没有的要进行安装

# 安装 gulp 命令行工具

npm install --global gulp-cli

创建项目目录并导航到其中

npx mkdirp my-project

cd my-project

在项目目录中创建package.json

npm init

devDependency中安装gulp包

npm install --save-dev gulp

验证你的gulp版本

gulp --version

# 创建 gulpfile.js

使用文本编辑器在项目根目录中创建一个名为 gulpfile.js 的文件,其中包含以下内容:

// gulpfile.js
function defaultTask(cb) {
  console.log('默认任务执行');
  cb();
}
exports.default = defaultTask;

测试一下

在项目目录中运行 gulp 命令:

gulp

你会看到控制台输出“默认任务执行”。

要运行多个任务,你可以使用 gulp <task> <othertask>

# gulpfiles.js

可以使用js来编写gulpfile里面的内容

gulpfile 是项目目录中标题为 gulpfile.js,当你运行 gulp 命令时会自动加载

在此文件中,你经常会看到 gulp API,例如 src()dest()series()parallel(),但可以使用任何普通 JavaScript 或 Node 模块。任何导出的函数都将被注册到 gulp 的任务系统中。

当然可以使用其他的语言(例如 TypeScript 或 Babel)编写 gulpfile

这样也被称为转译

# 分割gulpfile

如果gulpfile中的neritic太多,可以将其重构为单独的文件。

每个任务都可以拆分成自己的文件,然后导入到 gulpfile 中进行组合

Node 的模块解析允许你将 gulpfile.js 文件替换为名为 gulpfile.js 的目录,该目录包含 index.js 文件,该文件被视为 gulpfile.js。然后,该目录可以包含你的各个任务模块

也可以这样:

gulpfile.js
gulp-tasks/
├── css.js
├── js.js
├── html.js
├── image.js
├── clean.js
├── serve.js

gulpfile.js 中统一引入:

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

const clean = require('./gulp-tasks/clean');
const css = require('./gulp-tasks/css');
const js = require('./gulp-tasks/js');
const html = require('./gulp-tasks/html');
const images = require('./gulp-tasks/image');
const serve = require('./gulp-tasks/serve');

exports.default = series(
  clean,
  parallel(css, js, html, images),
  serve
);

# 创建任务

每个 gulp 任务都是一个异步 JavaScript 函数

# 导出

任务可以被视为公共或私有

  • 公共任务从 gulpfile 中导出,这允许它们通过 gulp 命令运行。
  • 私有任务供内部使用,通常用作 series()parallel() 组合的一部分。

私有任务的外观和行为与任何其他任务类似,但终端用户无法独立执行它。要公开注册任务,请从 gulpfile 中导出它。

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

function clean(cb){
    cb();
}

function build(cb){
    cb();
}

export.build = build;
export.defalut = series(clean, build);
~/gulp/docs
$ gulp --tasks
[11:14:59] Tasks for ~/gulp/docs/gulpfile.js
[11:14:59] |-- buiid
[11:14:59] |-- default
[11:14:59]    | -- series>
[11:14:59]    | -- clean
[11:14:59]    | -- build

过去,task() 用于将你的函数注册为任务。虽然该 API 仍然可用,但导出应该是主要的注册机制,除非在导出不起作用的边缘情况下。

# 撰写任务

Gulp 提供了两种强大的组合方法,series()parallel(),允许将单个任务组合成更大的操作

两种方法都接受任意数量的任务函数或组合操作。series()parallel() 可以自行嵌套或彼此嵌套任意深度。

要让任务按顺序执行,请使用 series() 方法。

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

function transpile(cb) {
  // body omitted
  cb();
}

function bundle(cb) {
  // body omitted
  cb();
}

exports.build = series(transpile, bundle);

要使任务以最大并发度运行,请将它们与 parallel() 方法结合起来

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

function javascript(cb) {
  // body omitted
  cb();
}

function css(cb) {
  // body omitted
  cb();
}

exports.build = parallel(javascript, css);

当调用 series()parallel() 时,任务立即组合。这允许组合的变化,而不是单个任务内的条件行为。

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

function minify(cb) {
  // body omitted
  cb();
}


function transpile(cb) {
  // body omitted
  cb();
}

function livereload(cb) {
  // body omitted
  cb();
}

if (process.env.NODE_ENV === 'production') {
  exports.build = series(transpile, minify);
} else {
  exports.build = series(transpile, livereload);
}

series()parallel() 可以嵌套到任意深度。

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

function clean(cb) {
  // body omitted
  cb();
}

function cssTranspile(cb) {
  // body omitted
  cb();
}

function cssMinify(cb) {
  // body omitted
  cb();
}

function jsTranspile(cb) {
  // body omitted
  cb();
}

function jsBundle(cb) {
  // body omitted
  cb();
}

function jsMinify(cb) {
  // body omitted
  cb();
}

function publish(cb) {
  // body omitted
  cb();
}

exports.build = series(
  clean,
  parallel(
    cssTranspile,
    series(jsTranspile, jsBundle)
  ),
  parallel(cssMinify, jsMinify),
  publish
);

当运行组合操作时,每个任务将在每次引用时执行

例如,在两个不同任务之前引用的 clean 任务将运行两次并导致不期望的结果。相反,重构要在最终组合中指定的 clean 任务。

比如这样的代码:

// This is INCORRECT
const { series, parallel } = require('gulp');

const clean = function(cb) {
  // body omitted
  cb();
};

const css = series(clean, function(cb) {
  // body omitted
  cb();
});

const javascript = series(clean, function(cb) {
  // body omitted
  cb();
});

exports.build = parallel(css, javascript);

迁移到这个:

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

function clean(cb) {
  // body omitted
  cb();
}

function css(cb) {
  // body omitted
  cb();
}

function javascript(cb) {
  // body omitted
  cb();
}

exports.build = series(clean, parallel(css, javascript));

# Gulp 常用 API 介绍

API 功能描述
src() 指定源文件流
pipe() 将处理流程连接起来
dest() 输出处理结果
watch() 监听文件变化
series() 串行执行任务
parallel() 并行执行任务

# 实战任务编写

下面是一个完整的 Gulp 项目模板,包含了前端开发中常见的构建任务:

✅ 编译 Sass ✅ 自动添加前缀 ✅ 压缩 CSS / JS ✅ 压缩图片 ✅ 自动刷新浏览器 ✅ 清理 dist 目录 ✅ 模块化 Gulp 构建结构

# 项目目录结构

gulp-template/
├── dist/                  # 构建输出目录
├── gulpfile.js            # Gulp 主入口
├── gulp-tasks/            # 模块化任务目录
│   ├── clean.js
│   ├── css.js
│   ├── js.js
│   ├── images.js
│   ├── serve.js
├── package.json
├── src/                   # 源码目录
│   ├── js/
│   │   └── main.js
│   ├── scss/
│   │   └── style.scss
│   ├── images/
│   │   └── test.jpg
│   └── index.html

# 初始化和安装依赖

  1. 初始化项目
mkdir gulp-template && cd gulp-template
npm init -y
  1. 安装依赖
npm install --save-dev gulp sass gulp-sass gulp-clean-css gulp-uglify gulp-autoprefixer gulp-imagemin browser-sync del

# 任务模块代码(放在 gulp-tasks 文件夹下)

  1. clean.js
const del = require('del');

function clean() {
  return del(['dist']);
}

module.exports = clean;

  1. css.js
const gulp = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const cleanCSS = require('gulp-clean-css');
const autoprefixer = require('gulp-autoprefixer');

function css() {
  return gulp.src('src/scss/**/*.scss')
    .pipe(sass())
    .pipe(autoprefixer({ overrideBrowserslist: ['last 2 versions'], cascade: false }))
    .pipe(cleanCSS())
    .pipe(gulp.dest('dist/css'));
}

module.exports = css;

  1. js.js
const gulp = require('gulp');
const uglify = require('gulp-uglify');

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

module.exports = js;

  1. images.js
const gulp = require('gulp');
const imagemin = require('gulp-imagemin');

function images() {
  return gulp.src('src/images/**/*')
    .pipe(imagemin())
    .pipe(gulp.dest('dist/images'));
}

module.exports = images;

  1. serve.js
const gulp = require('gulp');
const browserSync = require('browser-sync').create();
const css = require('./css');
const js = require('./js');

function serve() {
  browserSync.init({
    server: {
      baseDir: './src' // 使用源文件目录以利开发
    },
    port: 3000
  });

  gulp.watch('src/scss/**/*.scss', css).on('change', browserSync.reload);
  gulp.watch('src/js/**/*.js', js).on('change', browserSync.reload);
  gulp.watch('src/*.html').on('change', browserSync.reload);
}

module.exports = serve;

# 主入口:gulpfile.js

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

const clean = require('./gulp-tasks/clean');
const css = require('./gulp-tasks/css');
const js = require('./gulp-tasks/js');
const images = require('./gulp-tasks/images');
const serve = require('./gulp-tasks/serve');

exports.default = series(
  clean,
  parallel(css, js, images),
  serve
);

exports.build = series(
  clean,
  parallel(css, js, images)
);

# 示例源码

src/index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Gulp Template</title>
  <link rel="stylesheet" href="../dist/css/style.css" />
</head>
<body>
  <h1>Hello Gulp</h1>
  <img src="../dist/images/test.jpg" alt="test" />
  <script src="../dist/js/main.js"></script>
</body>
</html>

📜 src/scss/style.scss

$color: #3498db;

body {
  background: $color;
  h1 {
    color: white;
  }
}

📜 src/js/main.js

console.log('Hello Gulp!');

# 启动和打包

启动开发环境

npx gulp
  • 会执行 clean + css/js/images + serve
  • 浏览器自动打开,监听源码变化热更新

构建生产环境

npx gulp build
  • 清理 dist
  • 编译压缩所有资源
  • 用于部署发布

想要的 bonus(可选加上)

功能 插件
HTML 压缩 gulp-htmlmin
合并文件 gulp-concat
重命名文件 gulp-rename
条件构建(env) gulp-if
错误处理不退出 gulp-plumber
源映射(调试) gulp-sourcemaps

# 总结

总之

Gulp 不是用来“打包模块”的,而是“处理构建任务”的。它适合非模块化、静态页面、jQuery 项目,也适合辅助现代构建工具做任务增强。

参考文档:快速开始 | Gulp.js 中文网 (opens new window)