我习惯了用 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
- 对于 TypeScript,重命名为
gulpfile.ts
并安装 ts-node (opens new window) 模块。 - 对于 Babel,重命名为
gulpfile.babel.js
并安装 @babel/register (opens new window) 模块。
这样也被称为转译
# 分割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
# 初始化和安装依赖
- 初始化项目
mkdir gulp-template && cd gulp-template
npm init -y
- 安装依赖
npm install --save-dev gulp sass gulp-sass gulp-clean-css gulp-uglify gulp-autoprefixer gulp-imagemin browser-sync del
# 任务模块代码(放在 gulp-tasks
文件夹下)
clean.js
const del = require('del');
function clean() {
return del(['dist']);
}
module.exports = clean;
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;
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;
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;
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 项目,也适合辅助现代构建工具做任务增强。