这是关于为 WordPress 主题开发创建 Gulp 工作流程的两部分系列文章的第一部分。 这第一部分涵盖了初始设置的很多内容,包括 Gulp 安装和我们要运行的任务概述。 如果您有兴趣了解任务的创建方式,请关注第二部分。
今年早些时候,我创建了一个 构建高级 WordPress 主题的课程。 在这个过程中,我想使用任务运行器来连接和压缩 JavaScript 和 CSS 文件。 我最终使用该任务运行器来自动执行了许多其他任务,这些任务使主题更加高效和可扩展。
由 Node 提供支持的两个最流行的任务运行器是 Gulp 和 Grunt。 在经过大量的研究后,我选择了 Gulp,它似乎有一种直观的方式来编写任务。 它使用 Node 流来操作文件,使用 JavaScript 函数来编写任务,而 Grunt 使用配置对象来定义任务——这对于某些人来说可能很好,但对我来说有点不舒服。 此外,Gulp 比 Grunt 快一些,因为这些 Node 流更快,对我来说,更快总是好事!
因此,我们将设置 Gulp 来完成 WordPress 主题开发中的很多繁重工作。 现在我们将介绍初始设置,然后在另一篇文章中详细介绍任务本身。
文章系列
- 初始设置 (本文)
- 创建任务
初始主题设置
那么,我们如何使用 Gulp 来为 WordPress 主题提供任务? 首先,假设我们的主题只包含 WordPress 要求的任何主题的两个文件:index.php
和 styles.css
。 当然,大多数主题可能包含比这多得多的文件,但这现在并不重要。
其次,假设我们的主要目标是创建有助于管理资产的任务,例如压缩 CSS 和 JavaScript 文件,将 Sass 编译为 CSS,以及将现代 JavaScript 语法(例如 ES6、ES7 等)转换为 ES5 以支持旧版浏览器。
我们的主题文件夹结构将如下所示
themefolder/
├── index.php
├── style.css
└── src/
├── images/
│ └── cat.jpg
├── js/
│ ├── components/
│ │ └── slider.js
│ └── bundle.js
└── scss/
├── components/
│ └── slider.scss
└── bundle.scss
除了这两个必需文件之外,我们唯一添加的是一个 src
目录,我们的原始未编译资产将位于其中。
在该 src
目录中,我们有一个 images
子目录以及其他用于 JavaScript 和 Sass 文件的子目录。 从那里开始,JavaScript 和 Sass 子目录被组织成将从各自的 bundle
文件中调用的组件。 例如,当我们的 JavaScript 任务运行时,bundle.js
将导入并包含 slider.js
,因此我们所有的代码都将连接到一个文件中。
确定 Gulp 任务
好的,接下来我们希望 Gulp 任务创建一个新的 dist
目录,所有已编译、压缩和连接的资产版本将在任务完成后分发到该目录。 尽管我们在本文中将此目录称为 dist
,因为它代表“distribution”的缩写,但实际上可以称它为任何名称,只要 Gulp 知道它的名称即可。 无论如何,这些都是将分发给最终用户的资产。 src
文件夹将只包含我们在开发过程中直接编辑的文件。
确定哪些 Gulp 任务最适合项目将取决于项目的特定需求。 某些任务在某些项目中非常有用,但在其他项目中则完全无关紧要。 我已经确定了以下内容,将在本文中进行介绍。 您会发现一两个任务在 WordPress 上下文中(例如 POT 任务)比其他任务更有用。 然而,其中大多数任务足够广泛,您可能会在许多使用 Gulp 处理 Sass、JavaScript 和图像资产的项目中看到它们。
- 样式任务: 此任务负责将
scss
子目录中的bundle.scss
文件编译到位于dist
目录的css
目录中的bundle.css
。 此任务还将压缩生成的 CSS 文件,以便在生产中使用时,它的大小尽可能小。
我们将在本文中讨论生产模式与开发模式。 请注意,我们不会创建连接 CSS 文件的任务。 bundle.scss
文件将充当我们要包含的所有 . <code>scss
文件的入口点。 换句话说,任何要包含在项目中的 Sass 或 CSS 文件,只需使用 @import
语句在 bundle.scss
文件中导入它们即可。 例如,在我们的示例文件夹中,我们可以使用 @import ./components/slider';
来导入 slider.scss
文件。 通过这种方式,在我们的任务中,我们只需编译和压缩一个文件(bundle.css
)。
- 脚本任务: 与样式任务类似,此任务将把
bundle.js
从 ES6 语法转换为 ES5,然后压缩该文件以进行生产。
我们只编译 bundle.js
。 我们要包含的任何其他 JavaScript 文件都将使用 ES6 导入语句 来完成。 但是,为了使这些导入语句在所有浏览器上都能正常工作,我们需要使用模块捆绑器。 我们将使用 webpack 作为我们的捆绑器。 如果这是您第一次使用它,这篇入门文章 是了解其作用和用途的好地方。
- 图像任务: 此任务只会将
src/images
中的图像复制到dist/images
,并在文件压缩到最小尺寸后发送到那里。 - 复制任务: 此任务负责复制任何不在
/src/images
、/src/js
或/src/scss
中的其他文件或文件夹,并将它们发布到dist
目录中。
请记住,src
文件夹将包含仅在开发期间使用的文件,并且不会包含在最终主题包中。 因此,除图像、JavaScript 和 Sass 文件之外的任何资产都需要复制到 dist
文件夹中。 例如,如果我们有一个 /src/fonts
文件夹,我们希望将其中的文件复制到 dist
目录中,以便它们包含在最终交付的主题中。
- POT 任务: 顾名思义,此任务将扫描主题中的所有 PHP 文件,并从文件中的
gettext
调用生成一个.pot
(即翻译)文件。 这是我们在此介绍的所有任务中最侧重于 WordPress 的一个。 - 监视任务: 此任务实际上会监视文件的更改。 当进行更改时,将根据更改的文件类型触发并执行某些任务。
例如,如果我们更改了一个 JavaScript 文件,那么脚本任务应该发挥其作用,然后最好是让浏览器自动刷新,以便我们可以看到这些更改。 此外,如果我们更改了 PHP 文件,那么我们只需刷新浏览器,因为 PHP 文件不依赖于项目中的任何其他任务。 我们将使用一个名为 Browsersync 的 Gulp 插件来处理浏览器刷新,但我们将在稍后介绍该插件和其他插件。
- 压缩任务: 正如您可能预期的那样,我们用来编写任务的所有插件都将使用 npm 进行管理。 因此,我们的主题文件夹将包含另一个文件夹,如
node_modules
,该文件夹反过来包含package.json
和其他定义项目依赖项的配置文件——这些文件和文件夹仅在开发期间需要。 在生产期间,我们可以取出主题所需的文件,并将不需要的开发文件留在后面。 这就是此任务要做的;它将创建一个仅包含运行主题所需文件的 ZIP 文件。
作为压缩任务的额外步骤,如果您正在创建打算发布到 WordPress.org 或 ThemeForest 等网站的主题,那么您可能已经知道主题中的所有函数都必须使用唯一的词缀。
function mythemename_enqueue_assets() {
// function body
}
因此,如果您正在创建很多主题,您需要轻松地在不同的主题中重复使用函数,但更改词缀以匹配主题名称,以防止冲突。 我们可以使用占位符词缀来为我们的函数添加词缀,然后在压缩任务中替换该占位符的所有实例。 例如,我们可以选择字符串 _themename
作为占位符,当我们压缩主题时,我们将把所有“_themename”字符串替换为实际的主题名称。
function _themename_enqueue_assets() {
// function body
}
这也适用于我们在文本域中使用主题名称的任何地方。
<?php _e('some string', '_themename'); ?>
- 开发任务: 此任务没有做任何新事情。 它在开发主题时运行。 它将清除
dist
文件夹,在开发模式下运行样式、脚本、图像和复制任务(即不压缩任何资产),然后监视文件更改以自动刷新浏览器。 - 构建任务: 此任务旨在构建我们的生产文件。它将执行与开发任务相同的清理和任务,但以生产模式(即在此过程中最小化资产)运行,并为翻译更新生成一个新的 POT 文件。运行后,我们的 `dist` 文件夹应包含准备分发的文件。
- 捆绑任务: 此任务将简单地运行构建任务,确保 `dist` 文件夹中的所有文件都已最小化并准备分发。然后,它将运行压缩任务,该任务将所有生产就绪文件和文件夹捆绑到一个 ZIP 文件中。我们想要一个 ZIP 文件,因为这是 WordPress 识别以提取和安装主题的格式。
以下是我们的任务完成后文件结构的样子
themefolder/
├── index.php
├── style.css
├── src/
└── dist/
├── images/
│ └── cat.jpg // after compression
├── js/
│ └── bundle.js // bundled with all imported files (minified in production)
└── scss/
└── bundle.scss // bundled with all imported files (minified in production)
现在我们知道了将在项目中使用的任务以及它们的作用,让我们开始将 Gulp 安装到项目中。
安装 Gulp
在安装 Gulp 之前,我们应该确保在我们的机器上安装了 Node 和 npm。我们可以在命令行中运行以下命令来做到这一点
node --version
npm --version
… 而且,我们应该获得此处所示的一些版本号

现在,让我们将命令行指向主题文件夹
cd path/to/your/theme/folder
… 然后运行此命令来初始化一个新的 npm 项目
npm init
这将提示我们一些选项。在我们的案例中,唯一重要的选项是 `package name` 选项。这就是可以提供主题名称的地方 - 其他所有内容都可以保持其默认设置。在选择主题名称时,请确保只使用小写字符和下划线,同时避免使用破折号和特殊字符,因为此主题名称将用于替换我们之前提到的函数占位符。
接下来安装 Gulp!首先,我们必须全局安装 Gulp 的命令行界面 (gulp-cli),以便我们可以在命令行中使用 Gulp。
npm install --global gulp-cli
之后,我们运行此命令以在主题目录中安装 Gulp 本身
npm install --save-dev gulp
在撰写本文时,Gulp 的当前稳定版本是 3.9.1,但版本 4.0 已在 项目存储库 中可用。
为了确保一切正确安装,我们将运行此命令
gulp --version
不错!看起来我们正在运行版本 4.0,这是撰写本文时的最新版本。

编写 Gulp 任务
Gulp 任务在名为 `gulpfile.js` 的文件中定义,我们需要创建该文件并将其放置到主题的根目录中。
Gulp 的核心是 JavaScript,因此我们可以定义一个简单的示例任务,该任务将某些内容记录到控制台。
var gulp = require('gulp');
gulp.task('hello', function() {
console.log('First Task');
})
在此示例中,我们通过调用 `gulp.task` 定义了一个新任务。此函数的第一个参数是任务的名称 ( `hello`),第二个参数是我们希望在命令行中输入该名称时运行的函数,在本例中,它应该将“First Task” 打印到控制台。
现在就做吧。
gulp hello
以下是我们得到的结果

如您所见,我们确实得到了我们想要的 `console.log('First Task')` 输出。但是,我们还收到一个错误,指出我们的任务没有完成。所有 Gulp 任务都需要告诉 Gulp 任务结束的位置,我们通过调用作为任务函数中第一个参数传递的函数来做到这一点,如下所示
var gulp = require('gulp');
gulp.task('hello', function(cb) {
console.log('First Task');
cb();
})
让我们再次尝试运行 `gulp hello`,我们应该得到相同的输出,但这次没有错误。
`cb()` 是一个 Node.js 回调函数,通常传递给异步函数。在某些情况下,我们不必调用它,例如当任务返回一个 promise 或一个 node stream 时。节点流是我们将在本文中的任务中使用的,这意味着我们将在整篇文章中经常看到它。
以下是一个返回 promise 的任务示例。在此任务中,我们不必调用 `cb()` 函数,因为 Gulp 已经知道任务将在 promise 解析或返回错误时结束
gulp.task('promise', function(cb) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, 300);
});
});
现在尝试运行“gulp promise”,任务将完成而不会返回任何错误。
最后,值得一提的是,Gulp 接受一个默认任务,该任务通过在命令行中单独键入 `gulp` 来运行。所需要的只是使用“default”作为任务名称参数。
gulp.task('default', function(cb) {
console.log('Default Task');
cb();
});
现在,在命令行中单独键入 `gulp` 将运行该任务。
哇!现在我们知道了编写 Gulp 任务的基础知识。
我们还可以做一件事来改进,那就是在 Gulpfile 中启用 ES6 语法。这将允许我们使用诸如 解构、导入语句和箭头函数等功能。
在 Gulpfile 中使用 ES6
在 Gulpfile 中使用 ES6 语法的第一步是将其从 `gulpfile.js` 重命名为 `gulpfile.babel.js`。如您所知, Babel 是编译器,它将 ES6 编译为 ES5。
因此,让我们通过运行以下命令来安装 Babel 及其一些必需的软件包
npm install --save-dev @babel/register @babel/preset-env @babel/core
之后,我们必须在主题文件夹中创建一个名为 `.babelrc` 的文件。此文件将告诉 Babel 使用哪个 预设 来编译我们的 JavaScript。 `.babelrc` 文件的内容将如下所示
{
"presets": ["@babel/preset-env"]
}
现在我们可以在 Gulpfile 中使用 ES6 了!以下是重写后的样子
import gulp from 'gulp';
export const hello = (cb) => {
console.log('First Task');
cb();
}
export const promise = (cb) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 300);
});
};
export default hello
如您所见,我们使用 import 而不是 require 导入 Gulp。实际上,根本不需要导入 Gulp!我仍然包含 import 语句来表明它可以代替 `require` 使用。我们允许跳过导入 Gulp,因为我们不必调用 `gulp.task` - 相反,我们只需要导出一个函数,此函数的名称将是任务的名称。此外,定义默认函数所需要的只是使用 `export default`。还要注意那些箭头函数!一切都变得更加简洁。
让我们继续并开始编写实际的任务。
开发与生产
正如我们之前提到的,我们需要创建两种模式: **开发** 和 **生产**。我们需要区分这两者的原因是,任务中的一些细节将很耗时且耗费内存,这只有在生产环境中才有意义。例如,样式任务需要最小化 CSS。但是,最小化既需要时间也需要内存——如果该过程在开发过程中每次有变化时都运行,那不仅没有必要,而且效率非常低下。在开发过程中,任务越快越好。
我们需要设置一个标志,指定任务应该在哪种模式下运行。我们可以使用一个名为 yargs 的包,它允许我们在运行命令时定义这些类型的参数。因此,让我们安装它并使用它
npm install --save-dev yargs
现在,我们可以像这样向我们的命令添加参数
gulp hello --prod=true
… 然后在 Gulpfile 中检索这些参数
import yargs from 'yargs';
const PRODUCTION = yargs.argv.prod;
export const hello = (cb) => {
console.log(PRODUCTION);
cb();
}
请注意,我们在命令中定义的值在 Gulpfile 中的 `yargs.argv` 对象和 `console.log(PRODUCTION)` 中可用。在我们的案例中,这将输出 `true`,因此 `PRODUCTION` 将成为决定函数是否在任务中运行的标志。
我们准备好了!
我们在这里涵盖了很多内容,但现在我们拥有了一切,可以开始为我们的 WordPress 主题开发编写任务。这恰好是本系列下一部分的唯一重点,敬请期待明天的内容。
酷文章。有一个很棒的开源项目可以直接开箱即用地完成许多这些事情,名为 WPGulp。您可以在 GitHub 上找到该项目: https://github.com/ahmadawais/WPGulp。它非常容易上手。
哇哦!我本来打算在这里提到我的 WPGulp 项目——看起来你抢先了一步。很高兴看到开发人员喜欢我的开源作品。这是一个协作项目,但 WPGulp 比较容易上手。
@Chris 很乐意在这里写关于它的文章。
和平!✌️
大门敞开!https://css-tricks.org.cn/guest-posting/
感谢您对此的提醒,Joseph。我们已经很了解 Gulp,并且为我们的项目创建了一个很棒的 gulpfile,但这消除了很多猜测。
很棒的开源项目,wordpress 是最好的网站构建器之一,适合网站管理员。
我认为这是我第一次有点理解 Gulp……谢谢!
这很棒,实际上缺少的是构建
index.php
、styles.css
和functions.php
。还要明确的是,themefolder
是wp-content/themes/
的子文件夹。您实际上是在 WordPress 中构建了它。您可能想查看 WP Rig。它使用 Gulp 并将现代开发实践应用于 WP 主题:https://github.com/wprig/wprig/tree/v2.0
看到相同的文章,其中所有内容(包括 gulpfile)都是用 Typescript 而不是 Babel 编写的,这将非常有用。
你能解释一下为什么吗?
您好,这只是您在使用 ES6 语法时如何定义 gulp 任务的方法,您从 gulpfile 中导出的任何函数都将被视为任务。您仍然可以使用 gulp.task,如果您愿意的话。
感谢 Ali Alaa 的回答,更多信息请参见:https://gulp.node.org.cn/docs/en/getting-started/creating-tasks
为什么 yargs 是一个开发依赖项?如果生产机器上没有安装 yargs,我们如何传递 –prod=true?
您只需要在开发期间传递 –prod 才能生成您的主题文件夹。为什么要在生成此文件夹后运行任何命令?
我似乎无法让 .babelrc 文件正常工作,每次尝试使用 ES6 时都会遇到相同的错误。
“Requiring external module @babel/register” 但我可以在我的 @babel 节点模块中看到 register 文件……
您好,我不明白。确切的错误是什么?