Gulp 是一个工具,可以帮助您完成 web 开发中的多种任务。它通常用于执行前端任务,例如
- 启动 web 服务器
- 在保存文件时自动重新加载浏览器
- 使用 Sass 或 LESS 等预处理器
- 优化 CSS、JavaScript 和图像等资源
重要说明! 本文是为 Gulp 3.x 编写的,但现在 Gulp 4.x 已发布并推荐使用。与任何主要版本更改一样,API 存在重大更改。如果您安装了 Gulp 4 并尝试执行本文中的一些操作,它们将无法正常工作。我们将在本文中添加一些备注以提醒您。 这是一个不错的 Gulp 4 启动程序。
这不是 Gulp 可以执行的操作的完整列表。如果您有足够的疯狂,甚至可以使用 Gulp 构建静态网站生成器(我曾经做过!)。因此,是的,Gulp 非常强大,但是如果您想创建自己的自定义构建流程,您必须学习如何使用 Gulp。
因此,这就是本文的目的。它可以帮助您掌握 Gulp 的基础知识,以便您可以开始自己探索其他一切。
在我们深入了解 Gulp 的工作原理之前,让我们先谈谈为什么您可能想要使用 Gulp 而不是其他类似的工具。
为什么选择 Gulp?
像 Gulp 这样的工具通常被称为“构建工具”,因为它们是用于运行构建网站任务的工具。当前最流行的两个构建工具是 Gulp 和 Grunt。(Chris 有一篇关于这里如何开始使用 Grunt 的文章)。当然还有其他工具。 Broccoli 专注于资产编译,这是最常见的构建工具任务之一。
已经有多篇文章介绍了 Grunt 和 Gulp 之间的区别,以及为什么您可能会选择其中一个而不是另一个。您可以查看这篇文章、这篇文章或这篇文章,如果您有兴趣了解更多。 Brunch 在其对资产的关注方面与它们类似,并且它捆绑了一些最常见的其他任务,例如服务器和文件监视器。
主要区别在于您如何使用它们配置工作流程。与 Grunt 相比,Gulp 配置往往更短、更简单。Gulp 的运行速度也更快。
现在让我们继续,了解如何使用 Gulp 设置工作流程。
我们正在设置的内容
在本教程结束时,您将拥有一个工作流程来执行我们在本文开头概述的任务
- 启动 web 服务器
- 将 Sass 编译为 CSS
- 在您保存文件时自动刷新浏览器
- 优化所有资源(CSS、JS、字体和图像)以供生产使用
您还将学习如何将不同的任务链接在一起,形成简单易懂且易于执行的命令。
让我们从将 Gulp 安装到您的计算机开始。
安装 Gulp
在安装 Gulp 之前,您需要在计算机上安装 Node.js(Node)。
如果您尚未安装 Node,您可以从Node 网站下载软件包安装程序获取它。
完成 Node 安装后,可以使用以下命令在命令行中安装 Gulp
$ sudo npm install gulp -g
注意:只有 Mac 用户需要 sudo 关键字。(查看 Pawel Grzybek 的第一条评论,如果您不想使用 sudo)。请记住,上面代码中的“$”只是代表命令提示符。这不是您实际运行的命令的一部分。
我们在这里使用的 npm install
命令是一个使用 Node Package Manager (npm) 将 Gulp 安装到您的计算机上的命令。
此命令中的 -g
标志告诉 npm 将 Gulp 全局安装到您的计算机上,这使您可以在系统上的任何位置使用 gulp
命令。
Mac 用户需要在命令中添加 sudo
关键字,因为他们需要管理员权限才能全局安装 Gulp。
现在您已经安装了 Gulp,让我们创建一个使用 Gulp 的项目。
创建一个 Gulp 项目
首先,我们将创建一个名为 project
的文件夹,作为我们本教程中的项目根目录。在该目录中运行 npm init
命令
# ... from within our project folder
$ npm init
npm init
命令为您的项目创建一个 package.json
文件,该文件存储有关项目的信息,例如项目中使用的依赖项(Gulp 是依赖项的一个示例)。
npm init
会提示您

创建 package.json
文件后,可以使用以下命令将 Gulp 安装到项目中
$ npm install gulp --save-dev
这次,我们正在将 Gulp 安装到 project
中,而不是全局安装它,这就是命令中存在一些差异的原因。
您会看到 sudo
关键字不再需要,因为我们不是全局安装 Gulp,所以 -g
也不需要。我们添加了 --save-dev
,它告诉计算机将 gulp
作为 package.json
中的开发依赖项添加。

如果在命令执行完毕后检查项目文件夹,您应该会看到 Gulp 创建了一个 node_modules
文件夹。您还应该在 node_modules
中看到一个 gulp
文件夹。

我们几乎准备好开始使用 Gulp 了。在开始之前,我们需要明确我们将如何使用 Gulp 来完成项目,而其中的一部分是决定目录结构。
确定文件夹结构
Gulp 足够灵活,可以与任何文件夹结构一起使用。您只需要了解其内部工作原理,然后才能针对您的项目进行调整。
在本教程中,我们将使用通用 web 应用程序的结构
|- app/
|- css/
|- fonts/
|- images/
|- index.html
|- js/
|- scss/
|- dist/
|- gulpfile.js
|- node_modules/
|- package.json
在此结构中,我们将使用 app
文件夹用于开发目的,而 dist
(表示“分发”)文件夹用于包含生产站点的优化文件。
由于 app
用于开发目的,因此我们的所有代码都将放置在 app
中。
在进行 Gulp 配置时,我们需要牢记此文件夹结构。现在,让我们从在 gulpfile.js
中创建您的第一个 Gulp 任务开始,该文件存储所有 Gulp 配置。
编写您的第一个 Gulp 任务
使用 Gulp 的第一步是在 gulpfile 中require
它。
var gulp = require('gulp');
require 语句告诉 Node 在 node_modules
文件夹中查找名为 gulp
的包。找到包后,我们将它的内容分配给变量 gulp
。
现在,我们可以使用此 gulp
变量开始编写 gulp 任务。gulp 任务的基本语法是
gulp.task('task-name', async function() {
// Stuff here
});
task-name
指的是任务的名称,该名称将在您想要在 Gulp 中运行任务时使用。您也可以在命令行中运行同一个任务,方法是编写 gulp task-name
。
为了测试它,让我们创建一个显示 Hello Zell!
的 hello
任务。
gulp.task('hello', async function() {
console.log('Hello Zell');
});
我们可以在命令行中使用 gulp hello
运行此任务。
$ gulp hello
命令行将返回一条显示 Hello Zell!
的日志。

Gulp 任务通常比这更复杂。它通常包含两个额外的 Gulp 方法,以及各种 Gulp 插件。
以下是一个真实任务的示例
gulp.task('task-name', async function () {
return gulp.src('source-files') // Get source files with gulp.src
.pipe(aGulpPlugin()) // Sends it through a gulp plugin
.pipe(gulp.dest('destination')) // Outputs the file in the destination folder
})
如您所见,真实任务接受两个额外的 gulp 方法——gulp.src
和 gulp.dest
。
gulp.src
告诉 Gulp 任务要使用哪些文件来执行任务,而 gulp.dest
告诉 Gulp 在任务完成后将文件输出到哪里。
让我们尝试构建一个真实的任务,在这个任务中我们将 Sass 文件编译为 CSS 文件。
使用 Gulp 进行预处理
我们可以借助名为 gulp-sass 的插件在 Gulp 中将 Sass 编译为 CSS。您可以使用 npm install
命令将 gulp-sass 安装到您的项目中,就像我们之前对 gulp
所做的那样。
我们还想使用 --save-dev
标志来确保 gulp-sass 被添加到 package.json
中的 devDependencies
中。
$ npm install gulp-sass --save-dev
在我们能够使用该插件之前,我们必须从 node_modules
文件夹中 require
gulp-sass,就像我们之前对 gulp
所做的那样。
var gulp = require('gulp');
// Requires the gulp-sass plugin
var sass = require('gulp-sass');
我们可以通过用 sass()
替换 aGulpPlugin()
来使用 gulp-sass。由于该任务旨在将 Sass 编译为 CSS,所以让我们将其命名为 sass
。
gulp.task('sass', async function(){
return gulp.src('source-files')
.pipe(sass()) // Using gulp-sass
.pipe(gulp.dest('destination'))
});
我们需要向 sass
任务提供源文件和目标,以便任务能够正常工作,所以让我们在 app/scss
文件夹中创建一个 styles.scss
文件。该文件将被添加到 gulp.src
中的 sass
任务中。
我们希望将最终的 styles.css
文件输出到 app/css
文件夹,这将是 gulp.dest
的 destination
。
gulp.task('sass', async function(){
return gulp.src('app/scss/styles.scss')
.pipe(sass()) // Converts Sass to CSS with gulp-sass
.pipe(gulp.dest('app/css'))
});
我们希望测试 sass
任务是否按预期工作。为此,我们可以在 styles.scss
中添加一个 Sass 函数。
// styles.scss
.testing {
width: percentage(5/7);
}
如果您在命令行中运行 gulp sass
,您应该现在能够看到在 app/css
中创建了一个 styles.css
文件。此外,它还包含 percentage(5/7)
被计算为 71.42857%
的代码。
/* styles.css */
.testing {
width: 71.42857%;
}
这就是我们知道 sass
任务工作的方式!
有时我们需要能够将多个 .scss
文件同时编译为 CSS。我们可以借助 Node glob 来实现这一点。
Node 中的 Globbing
Glob 是用于文件的匹配模式,允许您将多个文件添加到 gulp.src
中。它类似于正则表达式,但专门用于文件路径。
当您使用 glob 时,计算机会检查文件名和路径以查找指定的模式。如果存在模式,则会匹配文件。
大多数使用 Gulp 的工作流程只需要 4 种不同的 globbing 模式
*.scss
:*
模式是一个通配符,匹配当前目录中的任何模式。在这种情况下,我们正在匹配根文件夹(project
)中以.scss
结尾的任何文件。**/*.scss
:这是*
模式的更极端版本,它匹配根文件夹和任何子目录中以.scss
结尾的任何文件。!not-me.scss
:!
表示 Gulp 应该从其匹配项中排除该模式,如果您需要从匹配模式中排除一个文件,这很有用。在这种情况下,not-me.scss
将被排除在匹配之外。*.+(scss|sass)
:加号+
和括号()
允许 Gulp 匹配多个模式,不同的模式用竖线|
字符分隔。在这种情况下,Gulp 将匹配根文件夹中以.scss
或.sass
结尾的任何文件。
既然我们了解了 globbing,我们可以用 scss/**/*.scss
模式替换 app/scss/styles.scss
,该模式匹配 app/scss
或子目录中任何以 .scss
结尾的文件。
gulp.task('sass', async function() {
return gulp.src('app/scss/**/*.scss') // Gets all files ending with .scss in app/scss and children dirs
.pipe(sass())
.pipe(gulp.dest('app/css'))
})
通过此更改,在 app/scss
中找到的任何其他 Sass 文件都将自动包含到 sass
任务中。如果您在项目中添加一个 print.scss
文件,您将看到 print.css
将在 app/css
中生成。

我们现在已经成功地使用单个命令将所有 Sass 文件编译为 CSS 文件。问题是,如果我们每次想要将 Sass 编译为 CSS 时都必须手动运行 gulp sass
,它有什么用呢?
幸运的是,我们可以告诉 Gulp 在每次保存文件时自动运行 sass
任务,这是一个名为“观察”的过程。
观察 Sass 文件以进行更改
注意!Gulp Watch 是从 3.x 到 4.x 发生重大变化的事情之一。 查看文档.
Gulp 为我们提供了一个 watch
方法,该方法检查是否保存了一个文件。watch
方法的语法是
// Gulp 3.x watch syntax
gulp.watch('files-to-watch', ['tasks', 'to', 'run']);
// NOTE! Gulp 4.x watch syntax (all the rest of the examples would need to be like this for Gulp 4
gulp.watch('files-to-watch', gulp.series(['tasks', 'to', 'run']));
如果我们想要观察所有 Sass 文件,并在每次保存 Sass 文件时运行 sass
任务,我们只需用 app/scss/**/*.scss
替换 files-to-watch
,用 ['sass']
替换 ['tasks', 'to', 'run']
// Gulp watch syntax
gulp.watch('app/scss/**/*.scss', ['sass']);
但更常见的是,我们希望一次观察不止一种类型的文件。为此,我们可以将多个观察进程组合到一个 watch
任务中
gulp.task('watch', async function(){
gulp.watch('app/scss/**/*.scss', ['sass']);
// Other watchers
})
如果您现在运行 gulp watch
命令,您会看到 Gulp 立即开始观察。

并且它会在您保存 .scss
文件时自动运行 sass
任务。

让我们更进一步,借助 Browser Sync,让 Gulp 在我们保存 .scss
文件时重新加载浏览器。
使用 Browser Sync 进行实时重新加载
Browser Sync 通过启动一个 Web 服务器来帮助简化 Web 开发,该服务器帮助我们轻松地进行实时重新加载。它还具有其他功能,例如 同步多个设备上的操作。
我们首先需要安装 Browser Sync
$ npm install browser-sync --save-dev
您可能已经注意到,我们在安装 Browser Sync 时没有 gulp-
前缀。这是因为 Browser Sync 与 Gulp 配合使用,所以我们不需要使用插件。
要使用 Browser Sync,我们必须 require
Browser Sync。
var browserSync = require('browser-sync').create();
我们需要创建一个 browserSync
任务来使 Gulp 能够使用 Browser Sync 启动一个服务器。由于我们正在运行一个服务器,我们需要让 Browser Sync 知道服务器的根目录应该在哪里。在我们的例子中,它是 app
文件夹
gulp.task('browserSync', async function() {
browserSync.init({
server: {
baseDir: 'app'
},
})
})
我们还必须稍微更改我们的 sass
任务,以便 Browser Sync 可以在每次运行 sass
任务时将新的 CSS 样式(更新 CSS)注入到浏览器中。
gulp.task('sass', async function() {
return gulp.src('app/scss/**/*.scss') // Gets all files ending with .scss in app/scss
.pipe(sass())
.pipe(gulp.dest('app/css'))
.pipe(browserSync.reload({
stream: true
}))
});
我们完成了配置 Browser Sync。现在,我们必须同时运行 watch
和 browserSync
任务,才能进行实时重新加载。
在两个命令行窗口中分别运行 gulp browserSync
和 gulp watch
会很麻烦,所以让我们让 Gulp 同时运行它们,方法是告诉 watch
任务,在 watch
允许运行之前,必须完成 browserSync
。
我们可以通过向 watch
任务添加第二个参数来实现这一点。语法是
gulp.task('watch', ['array', 'of', 'tasks', 'to', 'complete','before', 'watch'], async function (){
// ...
})
在这种情况下,我们添加了 browserSync 任务。
gulp.task('watch', ['browserSync'], async function (){
gulp.watch('app/scss/**/*.scss', ['sass']);
// Other watchers
})
我们还希望确保 sass
在 watch
之前运行,这样每次我们运行 Gulp 命令时,CSS 已经是最新的了。
gulp.task('watch', ['browserSync', 'sass'], async function (){
gulp.watch('app/scss/**/*.scss', ['sass']);
// Other watchers
});
现在,如果您在命令行中运行 gulp watch
,Gulp 应该同时启动 sass
和 browserSync
任务。当这两个任务都完成后,watch
将运行。

同时,指向 app/index.html
的浏览器窗口也会弹出。如果您更改 styles.scss
文件,您会看到浏览器会自动重新加载。

在我们结束这个实时重新加载部分之前,还有一件事。既然我们已经在观察 .scss
文件以进行重新加载,为什么不更进一步,在保存任何 HTML 或 JavaScript 文件时重新加载浏览器呢?
我们可以通过添加另外两个观察进程,并在保存文件时调用 browserSync.reload
函数来实现这一点
gulp.task('watch', ['browserSync', 'sass'], async function (){
gulp.watch('app/scss/**/*.scss', ['sass']);
// Reloads the browser whenever HTML or JS files change
gulp.watch('app/*.html', browserSync.reload);
gulp.watch('app/js/**/*.js', browserSync.reload);
});
到目前为止,在本教程中,我们已经处理了三件事
- 启动用于开发的 Web 服务器
- 使用 Sass 预处理器
- 在每次保存文件时重新加载浏览器
让我们在下一节中介绍优化资产的部分。我们将从优化 CSS 和 JavaScript 文件开始。
优化 CSS 和 JavaScript 文件
当我们尝试优化 CSS 和 JavaScript 文件以供生产使用时,开发人员需要执行两个任务:缩小和合并。
开发人员在自动化此过程时遇到的一个问题是,很难按正确顺序合并脚本。
假设我们在 index.html
中包含了 3 个脚本标签。
<script src="js/lib/a-library.js"></script>
<script src="js/lib/another-library.js"></script>
<script src="js/main.js"></script>
这些脚本位于两个不同的目录中。使用像 gulp-concatenate 这样的传统插件很难合并它们。
谢天谢地,有一个有用的 Gulp 插件 gulp-useref 可以解决这个问题。
Gulp-useref 通过查找以“<!–build:”开头并以“<!–endbuild–>”结尾的注释,将任意数量的 CSS 和 JavaScript 文件连接成单个文件。其语法如下:
... HTML Markup, list of script / link tags.
可以是
js
、css
或 remove
。最好将 type
设置为你尝试连接的文件类型。如果你将 type
设置为 remove
,Gulp 将删除整个构建块,而不会生成文件。
在这里指的是生成的文件的目标路径。
我们希望最终的 JavaScript 文件在 js
文件夹中生成,名为 main.min.js
。因此,标记将是
<script src="js/lib/a-library.js"></script>
<script src="js/lib/another-library.js"></script>
<script src="js/main.js"></script>
现在让我们在 gulpfile 中配置 gulp-useref 插件。我们必须安装该插件并在 gulpfile 中引入它。
$ npm install gulp-useref --save-dev
var useref = require('gulp-useref');
设置 useref
任务与我们迄今为止完成的其他任务类似。以下是代码
gulp.task('useref', async function(){
return gulp.src('app/*.html')
.pipe(useref())
.pipe(gulp.dest('dist'))
});
现在,如果你运行此 useref
任务,Gulp 将遍历 3 个脚本标签并将它们连接到 dist/js/main.min.js
中。

但是,该文件目前没有进行压缩。我们必须使用 gulp-uglify 插件来帮助压缩 JavaScript 文件。我们还需要一个名为 gulp-if 的第二个插件,以确保我们只尝试压缩 JavaScript 文件。
$ npm install gulp-uglify --save-dev
// Other requires...
var uglify = require('gulp-uglify');
var gulpIf = require('gulp-if');
gulp.task('useref', function(){
return gulp.src('app/*.html')
.pipe(useref())
// Minifies only if it's a JavaScript file
.pipe(gulpIf('*.js', uglify()))
.pipe(gulp.dest('dist'))
});
现在,只要你运行 useref
任务,Gulp 就会自动压缩 main.min.js
文件。
Gulp-useref 还有一个我尚未透露的巧妙之处,它会自动将“<!–build:”和“<!–endbuild–>”之间的所有脚本更改为指向 js/main.min.js
的单个 JavaScript 文件。

是不是很神奇?
我们也可以使用相同的方法连接任何 CSS 文件(如果你决定添加多个)。我们将遵循相同的步骤并添加 build
注释。
我们还可以压缩连接的 CSS 文件。我们需要使用一个名为 gulp-cssnano 的插件来帮助我们进行压缩。
$ npm install gulp-cssnano
var cssnano = require('gulp-cssnano');
gulp.task('useref', async function(){
return gulp.src('app/*.html')
.pipe(useref())
.pipe(gulpIf('*.js', uglify()))
// Minifies only if it's a CSS file
.pipe(gulpIf('*.css', cssnano()))
.pipe(gulp.dest('dist'))
});
现在,只要你运行 useref
任务,你就会得到一个优化的 CSS 文件和一个优化的 JavaScript 文件。
接下来,让我们优化图片。
优化图片
你可能已经猜到了,我们需要使用 gulp-imagemin 来帮助我们优化图片。
$ npm install gulp-imagemin --save-dev
var imagemin = require('gulp-imagemin');
我们可以使用 gulp-imagemin 来压缩 png
、jpg
、gif
甚至 svg
。让我们为此优化过程创建一个 images
任务。
gulp.task('images', async function(){
return gulp.src('app/images/**/*.+(png|jpg|gif|svg)')
.pipe(imagemin())
.pipe(gulp.dest('dist/images'))
});
由于不同的文件类型可以以不同的方式进行优化,因此你可能需要向 imagemin
添加选项来自定义如何优化每个文件。
例如,你可以通过将 interlaced
选项键设置为 true
来创建 隔行扫描 的 GIF。
gulp.task('images', async function(){
return gulp.src('app/images/**/*.+(png|jpg|jpeg|gif|svg)')
.pipe(imagemin({
// Setting interlaced to true
interlaced: true
}))
.pipe(gulp.dest('dist/images'))
});
如果你愿意,也可以尝试其他选项。
但是,优化图片是一个非常缓慢的过程,除非必要,否则你不想重复进行。为此,我们可以使用 gulp-cache 插件。
$ npm install gulp-cache --save-dev
var cache = require('gulp-cache');
gulp.task('images', async function(){
return gulp.src('app/images/**/*.+(png|jpg|jpeg|gif|svg)')
// Caching images that ran through imagemin
.pipe(cache(imagemin({
interlaced: true
})))
.pipe(gulp.dest('dist/images'))
});
我们几乎完成了优化过程。还有一个文件夹需要从 app
目录转移到 dist
目录,即字体目录。现在让我们来做这件事。
将字体复制到 Dist
由于字体文件已经过优化,所以我们不需要做任何其他事情。我们只需要将字体复制到 dist
目录中。
我们可以使用 Gulp 复制文件,方法是简单地指定 gulp.src
和 gulp.dest
,而无需任何插件。
gulp.task('fonts', async function() {
return gulp.src('app/fonts/**/*')
.pipe(gulp.dest('dist/fonts'))
})
现在,只要你运行 gulp fonts
,Gulp 就会将 app
中的 fonts
复制到 dist
中。

现在,gulpfile 中有 6 个不同的任务,每个任务都必须使用命令行单独调用,这有点麻烦,所以我们希望将所有任务绑定到一个命令中。
但是,在执行此操作之前,让我们看看如何自动清理生成的 files。
自动清理生成的 files
由于我们正在自动生成 files,因此我们希望确保不再使用的 files 不会在我们不知情的情况下保留在任何地方。
此过程称为清理(或简单来说,删除 files)。
我们必须使用 del 来帮助我们进行清理。
npm install del --save-dev
var del = require('del');
del
函数接受一组节点 glob,这些 glob 指示它要删除哪些文件夹。
使用 Gulp 任务进行设置与我们第一个“hello”示例几乎一样。
gulp.task('clean:dist', async function() {
return del.sync('dist');
})
现在,只要运行 gulp clean:dist
,Gulp 就会为你删除 dist
文件夹。
注意:我们不必担心删除 dist/images
文件夹,因为 gulp-cache 已经将图片的缓存存储在你的本地系统上了。
要清除本地系统的缓存,你可以创建一个名为 cache:clear
的单独任务
gulp.task(‘cache:clear’, async function (callback) {
return cache.clearAll(callback)
})
哇,真是一大堆。现在让我们将所有任务组合在一起!
组合 Gulp 任务
让我们总结一下我们所做的工作。到目前为止,我们已经创建了两个不同的 Gulp 任务集。
第一个集合用于开发过程,我们在此过程中将 Sass 编译为 CSS,监视更改,并相应地重新加载浏览器。
第二个集合用于优化过程,我们在此过程中准备好所有文件以用于生产网站。在此过程中,我们优化了 CSS、JavaScript 和图片等资产,并将字体从 app
复制到 dist
。
我们已经使用 gulp watch
命令将第一组任务组合到一个简单的流程中。
gulp.task('watch', ['browserSync', 'sass'], function (){
// ... watchers
})
第二组包含我们需要运行以创建生产网站的任务。这包括 clean:dist
、sass
、useref
、images
和 fonts
。
如果我们遵循相同的思路,就可以创建一个 build
任务将所有内容组合在一起。
gulp.task('build', [`clean`, `sass`, `useref`, `images`, `fonts`], async function (){
console.log('Building files');
})
不幸的是,我们无法以这种方式编写 build
任务,因为 Gulp 会同时激活第二个参数中的所有任务。
useref
、images
甚至 fonts
可能在 clean
完成之前完成,这意味着整个 dist
文件夹将被删除。
因此,为了确保清理在其他任务完成之前完成,我们需要使用一个名为 Run Sequence 的额外插件。
$ npm install run-sequence --save-dev
以下是使用运行序列执行任务序列的语法
var runSequence = require('run-sequence');
gulp.task('task-name', async function(callback) {
runSequence('task-one', 'task-two', 'task-three', callback);
});
当调用 task-name
时,Gulp 将首先运行 task-one
。当 task-one
完成后,Gulp 将自动启动 task-two
。最后,当 task-two
完成后,Gulp 将运行 task-three
。
Run Sequence 还允许你同时运行任务,只要将它们放在数组中即可
gulp.task('task-name', async function(callback) {
runSequence('task-one', ['tasks','two','run','in','parallel'], 'task-three', callback);
});
在这种情况下,Gulp 首先运行 task-one
。当 task-one
完成后,Gulp 将同时运行第二个参数中的每个任务。第二个参数中的所有任务都必须完成,然后才能运行 task-three
。
因此,我们现在可以创建一个任务,确保 clean:dist
首先运行,然后运行所有其他任务
gulp.task('build', async function (callback) {
runSequence('clean:dist',
['sass', 'useref', 'images', 'fonts'],
callback
)
})
为了保持一致性,我们也可以使用第一组构建相同的序列。这次让我们使用 default
作为任务名称
gulp.task('default', async function (callback) {
runSequence(['sass','browserSync', 'watch'],
callback
)
})
为什么是 default
?因为当你有一个名为 default
的任务时,你只需键入 gulp
命令即可运行它,这样可以节省一些按键操作。
最后,这是一个 GitHub 仓库,其中包含我们完成的所有工作!
总结
我们已经学习了 Gulp 的绝对基础知识,并创建了一个工作流程,该流程能够将 Sass 编译为 CSS,同时监视 HTML 和 JS 文件的更改。我们可以使用命令行中的 gulp
命令运行此任务。
我们还构建了第二个任务 build
,该任务为生产网站创建一个 dist
文件夹。我们已将 Sass 编译为 CSS,优化了所有资产,并将必要的文件夹复制到 dist
文件夹中。要运行此任务,我们只需在命令行中键入 gulp build
即可。
最后,我们有一个 clean
任务,可以清除生成的 dist
文件夹中的任何图片缓存,使我们能够删除任何无意中保留在 dist
中的旧 files。
到目前为止,我们已经创建了一个强大的工作流程,足以满足大多数 Web 开发人员的需求。我们还可以探索更多 Gulp 和工作流程,以使此过程更加完善。以下是一些供你参考的建议
用于开发
- 使用 Autoprefixer 编写无供应商的 CSS 代码
- 添加 Sourcemaps 以便于调试
- 使用 sprity 创建 Sprites
- 仅编译已更改的文件,使用 gulp-changed
- 使用 Babel 或 Traceur 编写 ES6
- 使用 Browserify、webpack 或 jspm 模块化 JavaScript 文件
- 使用模板引擎如 Handlebars 或 Swig 模块化 HTML
- 使用 require-dir 将 gulpfile 拆分成更小的文件
- 使用 gulp-modernizr 自动生成 Modernizr 脚本
优化
除了开发或优化流程,还可以使用 gulp-jasmine 编写 JavaScript 单元测试,甚至使用 gulp-rync 自动将 dist
文件夹部署到生产服务器上。
如您所见,即使我们创建的工作流程做了很多事情,但还有很多事情可以做。创建适合您需求的工作流程非常令人兴奋且令人满足,但如果您是新手,则可能需要花费很多时间。
Gulp 的功能太多,无法在这篇博文中或一系列博文中全部涵盖。这就是我写一本关于 自动化工作流程 的书的原因,如果您有兴趣了解更多信息,欢迎您 免费获取十章 :)
请在下面的评论中告诉我您对这篇文章的感受!是的,如果您对工作流程有任何疑问,请随时 与我联系。我很乐意回复!
正如我在 Zell 的博客中提到的,使用 `sudo` 通过 npm 安装全局包可能很危险。我最近发布了一篇关于它的博文
http://studiorgb.uk/fix-priviliges-and-never-again-use-sudo-with-npm/
同样的修复方法在 npm 网站上也有
https://docs.npmjs.net.cn/getting-started/fixing-npm-permissions
酷。感谢再次指出这一点 :)
嘿!我喜欢这个教程,我打算很快创建自己的 gulp 工作流程。关于 browsersync 监视功能,我有一个简单的问题。如果我正在处理一个 WordPress 项目,我需要它监视 `.php` 文件的更改,对吧?所以我需要将
gulp.watch('app/*.html', browserSync.reload);
更改为:gulp.watch('app/*.php', browserSync.reload);
迫不及待想开始使用 Gulp :)
是的,没错。
还有一件事。如果您使用 WordPress,您可能会使用 MAMP 作为您的服务器,因此您需要使用 `proxy` 键将 browserSync 指向您的服务器。查看我书中的免费示例章节以了解更多信息 :)
很棒的文章 - 很多有用的信息以一种对初学者和经验丰富的开发人员都有意义的方式呈现。
我注意到在前几个例子(比如 Sass 的东西)中,您没有从 `gulp` 任务中返回任何东西,并且您没有调用回调参数。如果既不执行这两件事,`gulp` 将不知道任务何时真正完成,如果它被用作另一个任务的依赖项,那就不好了。
一般来说,始终返回流(或 Promise),或调用回调参数。查看 `gulp` 文档中的注释。
我还注意到您使用的是 `runSequence`,这通常是不必要的(事实上,正如作者所说,整个事情是一个临时的解决方法)。对于初学者来说,重要的是要了解 `gulp` 任务只是普通的 JavaScript 函数。您可以像任何其他 JavaScript 函数一样组合它们。
关键是要记住,正如前面提到的,要正确处理异步任务。
另一件事是考虑如何组织您的 gulpfile.js,尤其是在不同项目之间。您可以创建自己的 npm 包,这样您就可以在一个中心位置管理您的 gulpfile.js,而不是每次都复制粘贴。这就是我们对 我们的资产管道 所做的,如果您有兴趣建立自己的管道,这可能是一个很好的例子。另一个很好的例子是 Laravel 的 Elixir。
哇!感谢 Agop。
错过了返回值。正在联系 Chris 现在进行更正
是的,我知道这是一个解决方法,但它比您刚刚编写的异步示例简单得多。此外,当 Gulp 4 发布时,他们将使用类似的语法。IMO,这不会造成太大伤害 :)
发布用于同步 Gulpfile 的 NPM 包真是一个好主意。太棒了,我之前没有想到!谢谢!:)
感谢您的文章。它清楚地解释了 gulp,而没有将其与同类工具 Grunt 进行比较。我有一个疑问,为什么在某些任务函数中使用回调,以及如何使用 task:varianttask,我是 gulp 的新手。
回调用于告诉 gulp.js 任务已完成。这对异步任务很重要,异步任务可能超出任务函数本身的持续时间。查看 gulp.js 文档中的异步任务部分。
task
和task:subTask
都是任务标识符(冒号对 gulp.js 没有任何意义)。有些人喜欢使用冒号来对相关任务进行分组。太棒了!感谢 Agop
不要使用 SUDO!!您不应该需要使用 root 权限来安装用户软件。以下是一个卸载 node 并重新安装它的脚本。非常简单,您就不需要使用 sudo 了
https://github.com/brock/node-reinstall
很棒的文章!Gulp 是一个超级有用的工具,我认为它比 Grunt 快得多。
没错!我上面也提到了。我也在博客上发布了一篇关于它的帖子:http://studiorgb.uk/fix-priviliges-and-never-again-use-sudo-with-npm/
很棒的文章。我昨天刚创建了我的第一个 gulpfile,这涵盖了我通过谷歌搜索了解到的很多东西。哈哈。感谢您的撰写。它给了我一些关于进一步优化的想法。我之前已经了解 Grunt 了,但我更喜欢 gulp。
不客气。在试用过它之后,我也更喜欢 Gulp
可能是一条重复的评论,但是的,安装了 Homebrew 后,不需要使用 sudo 进行 npm -g 安装。我是一个全职的 Grunt 用户,但我感谢您深入介绍了 Gulp,以防我最终参与使用它的项目构建流程。干得好!
看起来有一个错别字。
webapp 的结构是“app/scss”。但任务是“gulp.src('app/sass/styles.scss')”。
无论如何,您做得很好。非常感谢!
感谢您指出这一点,Sasha
嘿 Zell… 非常棒的文章… 感觉就像有人坐在旁边指导解释所有细节… 在 Twitter 上关注您!
太棒了!您可能还想查看我书中的 10 章免费内容。:)
这篇文章太棒了,令人难以置信 O_O。
感谢您的撰写。
不客气。:)
您可以添加 gulp 插件的自动加载功能,这非常方便:D
我不确定我是否做错了什么,但“gulp build”给了我这个错误
除此之外,很棒的文章,绝对值得了解 Gulp 的更多信息 :)
嘿,我发现在我写这篇文章后,`del` 发生了重大更改(v2.0)。您需要做的是使用以下命令安装 v1.2.1 而不是最新版本的 del
然后,一切都应该可以正常工作。
感谢您提供了很棒的教程。对我来说,您几乎涵盖了我的所有需求 :)
奇怪的是,直到“clean:dist”任务之前,一切都正常。当运行“gulp clean:dist”时,它总是启动任务,但从不完成。结果是:“build”任务也不起作用。
当我从 GitHub 克隆你的版本并运行任务时,它可以正常工作。当我将你的 gulpfile.js 复制到我的解决方案中时,它出现了与我相同的问题。我错过了什么?
我的仓库在 https://github.com/tomknapen/GulpForBeginners
你能确认 Mitranim 说的是否属实吗?我自己还没有使用 Gulp 4.0,我不确定它对这里列出的工作流程影响有多大。(可能只有 run-sequence 部分)。
嗨,我发现 `del` 在我写这篇文章之后有了重大更改(v2.0)。你需要做的是使用以下命令安装 v1.2.1 而不是 `del` 的最新版本。
然后,一切都应该可以正常工作。
嗨,
我无法让 “clean:dist” 任务工作。我已经安装了 [email protected],正如 Zell 指出的那样,但是任务不起作用。当我输入 “gulp clean:dist” 时,它会删除 “dist” 目录中的所有内容,包括图像文件夹。
有人设法解决了这个问题吗?
谢谢!
Bernat 你按照我提到的顺序安排了你的清理任务了吗?如果你这样做了,请将你的仓库发布到 Github 上并给我一个链接。我会看一下。
嗨 Zell,
这是我的 gulpfile.js: https://github.com/Zubrik/gulpfile/blob/master/gulpfile.js
谢谢。
Gulpfile 看起来是正确的。请给我整个仓库的链接?我需要测试一下。
你应该使用 Gulp 4.0(alpha 版本)和第三方 `gulp-watch` 库,而不是这篇文章推荐的 Gulp 3.x 和 `gulp.watch`。Gulp 4.0 具有内置方法来显式地按顺序或并行运行任务,而 `gulp-watch` 会在构建系统运行时注意到添加的文件。
太棒了 Zell!作为你自动化工作流程书籍的幸运校对者,我发现这篇文章很好地反映了你的书籍内容。清晰、精确、整洁 :-)
这太棒了!一个问题,我如何用 Gulp Sass 压缩编译后的 CSS?我查看了文档并自己尝试了一下,但编译后的 CSS 没有压缩。我这样尝试的
gulp.task(‘sass’ , function(){
return gulp.src(‘app/scss/style.scss’)
.pipe(sass({
style: ‘compressed’
}))
.pipe(gulp.dest(‘app/’))
.pipe(browserSync.reload({
stream:true
}))
})
是的,你可以。使用 `outputStyle: 'compressed'` 而不是 `style: ‘compressed’`
内容全面,结构清晰,一如既往,非常感谢 Zell!
我是否遗漏了关于 gulp-sourcemaps 跟踪的内容?在最小化之前,del 会清除 CSS 文件末尾的那些长长的乱码行吗?
Gulp-minify-css 会在生产环境的 CSS 中删除源映射行 :)
太棒了!感谢你的写作。
“首先,我们将创建一个名为 project 的文件夹作为我们的项目根目录,以便我们在本教程中进行操作。从该目录中运行 npm init 命令:”
这个 bash 命令如何知道文件夹的位置?你需要一个路径名吗?正如 Zell 指出的那样,Mac 用户通常使用 MAMP,但我无法在你的教程中找到任何关于如何在你的教程中运行该命令的信息……
获取我在文章末尾提供的 10 个免费章节。其中一章涉及学习命令行,以便你了解如何在何处使用这些 bash 命令。
如果你在阅读完之后仍然有问题,请告诉我。
package.json 变得过时后,插件是如何更新的?我已经尝试过 $npm i 和 $npm i –save-dev。与你的更新一些的插件相比,我的 package.json 中的所有插件都保持着相同的老旧版本……
我会在单独的文章中讨论这个问题。你也可以在我的书的结尾找到这些信息。
基本上,使用 `npm update` 或 npm-check-updates 包。
还有两个问题,请
1) “build” 任务中的 “sass” 函数在做什么?在我看来,到那时应该已经编译完所有内容了。
2) 我应该将所有 .html 文件放在根文件夹 “app” 中才能正确运行 “useref” 吗?我可以将所有 .html 文件(除了 index.html)放到 html 文件夹中吗?
可能存在进行更改但尚未编译的情况。在 build 任务中放置 `sass` 可确保它肯定已编译。
是的,你可以,只需使用节点 glob 来获取它。但是你必须注意 CSS 和 JavaScript 文件的路径。
嗨,感谢你的教程,它非常有用!
我可以成功地设置所有内容并创建一个 “gulp” 任务来完成所有工作。我唯一在控制台中看到的是,当构建过程运行时,我无法像对其他项目中使用的其他 Gulp 进程那样,通过 Ctrl+C 停止它。该进程已停止,但控制台中仅显示此内容:(^C^C^C^C)
[BS] 从以下位置提供文件:app
[14:23:39] “sass” 任务在 150 毫秒后完成。
[14:23:39] 开始 “watch” 任务……
[14:23:39] “watch” 任务在 12 毫秒后完成。
[14:23:39] “default” 任务在 165 毫秒后完成。
^C^C^C^C
问题是,如果我这样做,我就无法再次编译,除非我关闭终端并重新打开它。我以为是 runSequesnce 任务出了问题。有什么想法吗?谢谢!
嗯,有趣。这是我第一次看到这样的情况。你可以通过电子邮件将你的仓库发给我,以便我仔细查看一下吗?
谢谢!我刚刚通过你的联系表格页面给你发送了一条包含我仓库的消息。
我大多数情况下使用 codekit 来完成这些功能,但我愿意尝试一下!如果我“最终”让所有东西都正常工作,它是否可以轻松地用于下一个新项目?
是的,可以。你只需要复制 gulpfle 和 package.json 文件(以及你的项目结构),然后运行 `npm install`。
有一些插件,比如 “uncss”,可以删除闲置的 CSS。很棒的想法!对于 html,是否可以保留“闲置 CSS”,但同时对于 .js 却处于活动状态?“uncss” 和类似的插件是否会考虑对 .js 的链接来检查样式是否有用?
我还没有看到任何作者定义过这一点。
在使用 uncss 时,你可以选择忽略某些 CSS 文件。我将在单独的博文中介绍这一点。
我的意思是某些 CSS 样式。
好的,好的,Zell,干得漂亮!看来还有很多理由写更高级的博客。
我可以完全确定,在将 scss 编译为 css 后,从 w3c 的角度来看,结果是纯有效的 css 吗?听起来很合理……
但 html 验证可能会起作用。
很棒的文章。我一直避免在 node 中使用任务运行器,因为我不喜欢认知负荷,所以一直都在 ruby 中进行所有与任务相关的工作……但我现在非常高兴能转向 Gulp,这篇文章成为了我做这件事的绝佳工具。
一个小小的挑剔……在设置我的应用程序结构时,我只是做了 $touch index.html,导致出现断开连接,因为文件中没有 HTML。也许在序言中添加一些关于创建初始 HTML 的内容会更好。
为什么你要将你的 SCSS 编译到 app/css 而不是 dist 目录?
不用了,我看到你用 gulp-useref 来做这件事了,抱歉。
不过我还有另一个问题。在开发过程中,你是测试 app 目录还是 dist 目录中未压缩和未合并的文件?在某些情况下,我遇到了将压缩和未压缩文件都写入 dist 目录的情况,我认为这些文件是从那里进行测试的,并且只有压缩文件在生产环境中使用。最佳实践是什么?
我尽可能地从
app
目录进行测试,因为它是源代码。没有真正的“最佳实践”。这完全取决于你的信仰,而你的信仰会相应地调整你的工作流程。这可能在评论中讨论得太多了。我在我写的书中对它进行了广泛的介绍,我想我只是在那里引用它。只要获取示例章节,你就会对我的信念有所了解。
听起来不错?
听起来不错!感谢你的建议。我目前正在尝试为我和我的团队开发自己的工作流程,所以这本书肯定很有用!
我只是好奇,当任务开始运行,比如 gulp watch 时,我该如何停止它们?我仍然可以输入终端,但没有任何反应。
你可以通过按下
ctrl
+c
来停止终端。你知道为什么“gulp useref”命令没有按预期工作吗?该命令运行时,控制台输出
它将 index.html 放入 dist 文件夹中,但没有生成任何“main.min.js”,就像文章中所说的那样。我可能在哪里出错,我已经检查过代码很多次了,我认为它是一样的。
gulp-useref 插件不会向命令行记录任何内容,所以这是正常的。可能你在某个地方犯了一个小错误。请创建一个 github 仓库并通过电子邮件向我发送一个链接。我会看一下。
给你:https://github.com/Paulie-P/LearningGulp
Leo,你的
app
文件夹中没有任何 javascript 文件,所以dist
文件夹中没有任何东西。在“gulp useref”之后,我的 js 文件被合并到一个文件中,并且 index.html 文件被更新了...但当我更改其中一个 js 文件时,什么也没有发生...我不知道该怎么办...
我是否每次都需要执行(watch、useref)?
啊,所有东西都到了你的设置中的一个新文件夹...实际上我正在更新源文件...
嘿,Zell,感谢你的精彩文章,我开始使用 gulp,多亏了你。
我习惯使用“assets”文件夹来存放 css、js、图像和字体。我在使用 useref 时遇到问题。我希望我的 css 和 js 到达 dist/assets,而 html 到达 dist 文件夹,但我不知道是否可以添加两个目标。我不喜欢用 hacky 的方法,例如,我可以为将 css 和 js 复制到 dist 创建任务,然后为从 dist 删除它们创建任务。有没有简单的方法来解决这个问题?
你只需要在 html 中设置你的路径,如下所示
useref 将自动将你的 js 输出到
assets
文件夹中。哇,感谢你如此迅速的回复。我试过了,但由于另一个 bug,它不起作用。我已经修复了它,现在一切正常了。谢谢。
感谢这个教程,我学到了很多。
不过有两件事
1) 你在安装 gulp-minify-css 时没有使用 –save-dev。你是故意这样做的还是只是一个错误?
2) 当我使用 build 命令时,gulp 会创建一个 dist 文件夹并将所有文件复制到它们应该在的位置。到目前为止一切都好。但是,当我第二次运行 build 命令时,Gulp 只会删除 dist 文件夹(因为它应该这样做),就是这样。它不会创建一个新的。
我的 clean 任务是这样的(我没有操作图像,因此没有使用缓存功能)
build 任务是这样的
我忘记提到所有任务都在运行,没有错误。至少 shell 是这样说的。
这是一个错别字
你的 clean 任务缺少一个回调。(如果你使用的是 del 1.2.1)
啊,好吧。我尝试添加一个回调,但之后任务在清理后就会停止。当然我忘记了方括号。非常感谢。
好吧,我高兴得太早了。它不起作用。它总是在清理后停止。
整个 gulpfile 可以在这里看到:http://codepen.io/tmfsd/pen/rOwJLx/
这仅适用于 del v1.2.1。你使用的是它吗?del 的最新版本是 2.0。
你说得对。它是 2.0.2 版本。但我认为我最好尝试用最新版本运行它,而不是安装旧版本。谢谢。