Appearance
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 与其他工具对比
| 特性 | Gulp | Webpack | Vite |
|---|---|---|---|
| 类型 | 任务运行器 | 模块打包器 | 构建工具 |
| 配置方式 | 代码 | 配置文件 | 配置文件 |
| 开发服务器 | 需插件 | 内置 | 内置 |
| 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-babel | Babel 编译 |
| gulp-typescript | TypeScript 编译 |
| gulp-sass | Sass 编译 |
| gulp-less | Less 编译 |
| gulp-postcss | PostCSS 处理 |
| gulp-terser | JS 压缩 |
| gulp-htmlmin | HTML 压缩 |
| gulp-imagemin | 图片压缩 |
| gulp-concat | 文件合并 |
| gulp-rename | 文件重命名 |
| gulp-sourcemaps | Source 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'));
}