Gulp是一个前端自动化构建工具,它的配置的编写非常简单易用而且支持大量插件,能够轻松的自动化完成前端构建任务。目前最新版本是Gulp4,这篇笔记我们以该版本为例介绍Gulp的使用。
Gulp和Webpack这两个工具的初衷是完全不同的。Gulp是一个基于流程(task)的构建工具,通过我们指定的若干task完成一个预期的工程构建;而webpack是一个打包(Bundle)工具,它通常用来将一些源码和资源打包,本身并没有什么流程的概念。
至于为什么各种前端框架的脚手架都不需要Gulp,因为它们都是纯前端SPA框架,项目构建比较简洁,也谈不上什么task,而且这个过程完全不需要开发者干预。Webpack丰富的插件也能方便的实现代码合并混淆压缩、ES6转义、各种Loader打包加载资源、HMR等功能,Gulp的功能点完全用不上。而如果是我们自己一步步写起来的项目,尤其是那种多页面、前后端不分离的大型项目,又有代码合并混淆压缩的需求,Webpack当然是办不到的,我们自然而然的会想到找一个“构建脚本”去干这件事,这时候就是Gulp出场的时候,甚至可能Gulp + Webpack的形式出场。
实际开发中,我们通常在工程中局部安装Gulp工具。
npm install gulp --save-dev
安装完Gulp后,我们还需要在项目根目录创建一个配置文件gulpfile.js
,构建流程需要在这个配置文件中配置。
下面是一个gulpfile.js
的例子,它没有实际意义,仅仅是演示如何定义Gulp构建流程。
const {series} = require('gulp');
function clean(cb) {
console.log('clean执行了');
cb();
}
function build(cb) {
console.log('build执行了');
cb();
}
exports.build = build;
exports.default = series(clean, build);
能够被gulp
命令调用的构建流程需要定义为一个函数并通过exports
暴露,上面代码中我们暴露了build
而没有暴露clean
,因此可以使用命令gulp build
,而调用gulp clean
则会报错。除此之外,exports.default
定义的是默认的构建流程,即直接执行gulp
命令不附带参数时的动作。
series
表示一个顺序执行的构建流程序列,它可以用来连接多个构建流程。之前代码中,如果执行命令gulp
,那么clean
和build
会依次顺序执行。除了series
,还有一个parallel()
用来连接并行的构建流程。
exports.build = series(
clean,
parallel(
cssTranspile,
series(jsTranspile, jsBundle)
),
parallel(cssMinify, jsMinify),
publish
);
如果有多个复杂的构建流程需要连接,在exports
的赋值语句中连接它们是推荐的写法。
构建过程其实就是读文件->处理文件->写文件
,src()
用来指定读取哪些文件,dest()
用来指定输出到哪里。
下面是一个将ES6文件用Babel转译为ES5的例子。
const {src, dest} = require('gulp');
const babel = require('gulp-babel');
function build() {
return src('src/*.js')
.pipe(babel({
presets: ['@babel/env']
}))
.pipe(dest('dist/'));
}
exports.build = build;
Gulp配置的一个构建流程中,每一个小步骤都要用pipe()
连接起来。
上面其实已经用到了我们非常熟悉的babel
插件,Gulp支持大量的高质量插件,我们可以在Gulp官网搜索其它插件。
在这些插件的npm仓库中,一般都会注明其用法。
前端开发本来没有编译构建这些繁琐的步骤,现在引入了Gulp,虽然可以用上各种现代化、工程化的特性了,但是如果每次修改一点代码就手动运行一次命令去构建代码就太麻烦了,gulp支持监测文件系统中的文件修改触发自动构建。
exports.default = function () {
watch('src/', build);
};
注:代码中的build
流程沿用之前的Babel转义功能。
这里我们为一个SpringBoot应用编写一个Gulp构建流程,后端应用出于业务复杂性和开发效率等多种因素考虑,并未使用现代化的框架和前后端分离模式开发而是使用Thymeleaf模板,这里我们需要用Gulp处理JavaScript和Less代码。我们打算使用watch
去实时构建,而不是在Maven或Gradle中调用Gulp。
gulpfile.js
const {src, dest, parallel, watch} = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const rename = require('gulp-rename');
const less = require('gulp-less');
const minifyCss = require('gulp-minify-css');
function buildLess() {
return src('static/src/*.less')
.pipe(less())
.pipe(minifyCss())
.pipe(dest('static/dist/'));
}
function buildJs() {
return src('static/src/*.js')
.pipe(babel({
presets: ['@babel/env']
}))
.pipe(uglify())
.pipe(rename({extname: '.min.js'}))
.pipe(dest('static/dist/'));
}
exports.default = function () {
watch('static/src/', parallel(buildJs, buildLess));
};
上面代码中,我们定义了两个并行的task,分别用来编译js
和less
。
<link th:href="@{/dist/style.css}" type="text/css" rel="stylesheet" />
<script th:src="@{/dist/app1.min.js}"></script>
<script th:src="@{/dist/app2.min.js}"></script>
在模板中,我们直接引入生成的.min.js
文件。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
为了在gulp
实时构建后,SpringBoot能够热部署,我们引入了devtools
的起步依赖。
这样,我们的目的就基本实现了。当然,其中的不完美之处还有很多,比如:
clean
任务,build
和clean
应单独暴露出来import
语法,这里其实我们应该再引入webpack
进行打包配合Gulp的构建任务看来要做到尽善尽美还是很复杂的,不过这里仅仅是演示gulp
的使用,这里就不多说了。从上面例子我们可以发现,gulp
作为一个前端构建工具确实是个神器。