Gulp与Grunt

本文最后更新于:2023年3月19日 晚上

本文转自:https://www.jianshu.com/p/1a255e740710https://juejin.im/post/6844903870456414216https://segmentfault.com/a/1190000019650765

1. Gulp

gulp 是基于 Nodejs 的自动任务运行器,能自动化的完成 javascript/coffee/sass/less/html/image/css 等文件的的测试、检查、合并、压缩、格式化、浏览器自动刷新、部署文件生成,并监听文件在改动后重复指定的这些步骤。

通俗来讲,gulp 就是一个处理项目文件的工具,通过 gulp 建立一些任务,当我们对相应文件进行修改之后,会自动触发这些任务,自动的对文件进行了处理。不用我们手动的、反复的去处理这些文件,大大提高了我们的工作效率。
举个栗子:
现在已经开始用 less 来写样式(less 的优点很多,写起来要比 css 快很多,在此不多说,盆友们可以上网了解 less),在 html 使用这些样式,需要用一些工具将.less 文件转换为.css 文件,如果用 Sublime 敲代码的盆友可能知道它有插件自动生成 css,但是.less 和生成的.css 文件在同一目录下。


目录.png

这只是文件比较少的情况,如果文件比较多呢?是不是也是一件很头疼的事情…
下面,上代码带你用 gulp 解决令人头疼的问题 ^ _ ^
前提我们已经创建好一个 nodejs 项目,这里我们安装两个插件: gulp 和 gulp-less(编译 less 文件)
npm install gulp --save-dev
npm install gulp-less --save-dev

新建 gulpfile.js 文件

1
2
3
4
5
6
7
8
9
10
// 说明:gulpfile.js是gulp项目的配置文件,放项目根目录即可。
// 导入工具包
var gulp = require('gulp'),
less = require('gulp-less');
// 定义一个testLess任务(自定义任务名称)
gulp.task('testLess', function () {
gulp.src('public/stylesheets/style.less') //该任务针对的文件
.pipe(less()) //该任务调用的模块
.pipe(gulp.dest('public/css')); //将会在public/css下生成style.css
});

接下来,我们只需要在命令行输入gulp testLess我们就可以在 public/css 下看到生成的 style.css 文件了
but 这显然是我们手动来生成的,我们需要的是让它自动生成,那好吧,现在我们需要一个东西能够来监听 less 文件的修改,然后自动去执行任务

1
2
3
gulp.task('testWatch', function () {
gulp.watch('public/stylesheets/*.less', ['testLess']); //当所有less文件发生改变时,调用testLess任务
});

好了,现在我们运行gulp testWatch当修改了 less 文件保存,会看到 css 文件也跟着变了^ _ ^
补充一点,我们在一个项目里使用 gulp 的时候,我们想启动项目,就自动启动了 testWatch,而不是启动项目之后,我们还要手动启动 testWatch,那么,加上下面代码:

1
gulp.task('default',['testWatch']); // 指定gulp默认启动任务

2. Grunt

Grunt 基于 Node.js ,用 JS 开发,这样就可以借助 Node.js 实现跨系统跨平台的桌面端的操作,例如文件操作等等。此外,Grunt 以及它的插件们,都作为一个 包 ,可以用 NPM 安装进行管理。

Grunt 依赖 Node.js 所以在安装之前确保你安装了 Node.js。然后开始安装 Grunt。
Grunt 可以帮助我们减少很多的工作量,比如:检查每个 JS 文件语法、合并两个 JS 文件、将合并后的 JS 文件压缩、将 SCSS 文件编译等,包括我们上面提到的试用 gulp 将.less 文件转换为.css 文件,grunt 也是可以实现的,下面我们用 grunt 实现 gulp 的转换 less 的功能:
和 gulp 一样,首先我们先安装 grunt 以及 grunt-contrib-less
npm install grunt --save-dev
npm install grunt-contrib-less --save-dev

新建 Gruntfile.js 文件

使用 grunt,主要有三块代码:任务配置代码、插件加载代码、任务注册代码。
任务配置代码就是调用插件配置一下要执行的任务和实现的功能,插件加载代码就是把需要用到的插件加载进来,任务注册代码就是注册一个 task,里面包含刚在前面编写的任务配置代码。
需要注意的是,grunt 的配置代码放到

1
2
3
module.exports = function(grunt) {
...
};

里面,没有为什么…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 1. 任务配置代码
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
//less插件配置
less: {
main: {
options: {
compress: false,
yuicompress: false
},
files: {
'./public/css/global.css': './public/less/*.less'
}
}
}
});
// pkg 功能是读取 package.json 文件,并把里面的信息获取出来,方便在后面任务中应用
// 2. 插件加载代码
grunt.loadNpmTasks('grunt-contrib-less');
// 3. 任务注册代码
grunt.registerTask('lessjs', ['less']);

到这一步,我们已经做好 grunt 配置文件了,控制台输入grunt lessjs即可实现同 gulp 一样的效果。
同样的,这一样使我们手动生成的,grunt 能像 gulp 一样自动为我们生成吗?答案是肯定的,grunt 同样也有 watch。
首先安装插件 grunt-contrib-watch
npm install grunt-contrib-watch --save-dev
在任务配置代码添加一个 watch 任务:

1
2
3
4
5
6
watch: {
lesses: {
files: ['./public/less/*.less'],
tasks: ['less']
}
}

同样需要加载插件代码,任务注册代码:

1
2
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['less', 'watch']);

3. grunt 与 gulp 区别

上面的学习,我们知道 grunt 和 gulp 能实现同样的功能,那么它们有什么区别?分别在什么场景下使用呢?
在我看来,其实 grunt 和 gulp 两个东西的功能是一样的,只不过是任务配置 JS 的语法不同,Gulp 配置文件的写法更加通俗易懂,上手更快。但是 Gulp 的插件感觉不如 Grunt,只能满足基本的工作需要;而 Grunt 官方提供了一些常见的插件,满足大部分日常工作,而且可靠值得信赖,而 Gulp 好像没有太多官方出品,各种插件不太规范。
至于在什么场景下使用哪个?这个看个人需要吧,grunt 远远不止上面写的那么简单,如果 gulp 能满足平时工作需要,可以使用 gulp。
总之,用网友的话说,Grunt 和 Gulp 就像 iPhone 与 Android 一样,一个质量高学习难一点,一个学起来简单但是有点那个,你懂得。

什么是 gulp?

gulp 文档上面有这么一句话 ,也就是说 gulp 是一个自动化构建工具; gulp 的一些功能如下(包括但不限于):

gulp 或 grunt 和 webpack 的区别

其实 Webpack 和另外两个并没有太多的可比性

  • Gulp/Grunt 是一种能够优化前端的开发流程的工具,而 WebPack 是一种模块化的解决方案,不过 Webpack 的优点使得 Webpack 在很多场景下可以替代 Gulp/Grunt 类的工具。

  • Grunt 和 Gulp 的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务。

  • Webpack 的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack 将从这个文件开始找到你的项目的所有依赖文件,使用 loaders 处理它们,最后打包为一个(或多个)浏览器可识别的 JavaScript 文件。
    上述内容转自@zhangwang 的入门 Webpack,看这篇就够了

gulp 起步

傻瓜式起步照搬官网文档 1.安装

1
2
3
4
5
6
7
// 全局安装
$ npm install -g gulp
或者
$ npm install --global gulp
// 作为项目的开发依赖(devDependencies)安装:
$ npm install --save-dev gulp
复制代码

2.在项目根目录下创建一个名为 gulpfile.js 的文件:

1
2
3
4
5
var gulp = require('gulp');
gulp.task('default', function() {
// 将你的默认的任务代码放在这
});
复制代码

3.运行 gulp:

1
2
$ gulp
复制代码

默认的名为 default 的任务(task)将会被运行,在这里,这个任务并未做任何事情。 具体详情可以查看gulpjs.com 文档

项目搭建

新建一个项目 gulp-test 环境:

1
2
3
$ node -v // v9.1.0
$ npm -v // 6.5.0
复制代码

1.新建文件以下文件如下

1
2
3
4
5
6
7
8
gulp-test/
css/
index.scss
js/
helloworld.js
index.html
gulpfile.js
复制代码

其中 gulpfile.js 是我们 gulp 的配置文件,启动 gulp 默认会找个这个文件并执行; 2.接下来安装依赖

1
2
$ npm init
复制代码

一直按回车 Enter 初始化 package.json 文件(小技巧: npm iniy -y 可以免去繁琐的 enter 步骤) 此时我们的目录结构是这样了

1
2
3
4
5
6
7
8
9
gulp-test/
css/
index.scss
js/
helloworld.js
index.html
gulpfile.js
package.json
复制代码

安装依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
npm i --save-dev gulp        // gulp自动化构建工具
npm i --save-dev gulp-uglify //js压缩
npm i --save-dev gulp-concat //文件合并
npm i --save-dev gulp-jshint //js语法检测
npm i --save-dev gulp-rename //文件重命名
npm i --save-dev gulp-sass //sass编译工具
npm i --save-dev gulp-minify-css //css压缩
npm i --save-dev del //文件删除
// 以下三选一
npm i --save-dev gulp-connect // 自动刷新页面
npm i --save-dev browser-sync // 自动刷新页面
npm i --save-dev gulp-livereload // 自动刷新页面
复制代码

这里页面实时刷新只讲这个gulp-connect ,其他详情可以参照Browsersync和文章gulp-livereload
安装完依赖后配置 gulpfile.js 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// 定义依赖项和插件
const gulp=require('gulp');
const uglify=require('gulp-uglify'); //js压缩
const concat=require('gulp-concat'); //文件合并
const jshint = require('gulp-jshint'); //js语法检测
const rename = require('gulp-rename'); // 重命名
const sass = require('gulp-sass'); // 编译scss
const minifycss = require('gulp-minify-css'); // 压缩css
// const livereload = require('gulp-livereload'); // 自动刷新页面
const del = require('del'); //文件删除
const connect = require('gulp-connect'); // 自动刷新页面
gulp.task('server', function() {
connect.server({
port: 8080, //指定端口号,在浏览器中输入localhost:8080就可以直接访问生成的html页面
root: './', //指定html文件起始的根目录
livereload: true //启动实时刷新功能(配合上边的connect.reload()方法同步使用)
});
});
// 定义名为 "my-task" 的任务压缩js
gulp.task('my-task-js', function(){
gulp.src('./js/*.js')
.pipe(jshint())
.pipe(uglify())
.pipe(concat('all.js'))
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('./dist/js'))
.pipe(connect.reload())
});
// 定义名为 "my-task-css" 的任务编译scss压缩css
gulp.task('my-task-css', function() {
gulp.src('./css/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(concat('all.css'))
.pipe(rename({suffix: '.min'}))
.pipe(minifycss())
.pipe(connect.reload())
.pipe(gulp.dest('./dist/css'))
});
gulp.task('html', function(){
gulp.src('*.html')
.pipe(gulp.dest('dist/html'))
.pipe(connect.reload())
})
//执行压缩前,先删除以前压缩的文件
gulp.task('clean', function() {
return del(['./dist/css/all.css', './dist/css/all.min.css', './dist/all.js','./dist/all.min.js', './dist/html'])
});
// 定义默认任务
gulp.task('default',['clean'],function() {
gulp.start('my-task-js', 'my-task-css', 'watch', 'server' );
});
// 任务监听
gulp.task('watch', function() {
// Watch.js files
gulp.watch('./js/*.js', ['my-task-js']);
// Watch .scss files
gulp.watch('./css/*.scss', ['my-task-css']);
// Watch .html files
gulp.watch('./*.html', ['html']);
// Watch any files in dist/, reload on change
// gulp.watch(['dist/!**']).on('change', livereload.changed);
});
复制代码

大概讲解一下 gulpfile.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
// ...
// 定义名为 "my-task" 的任务压缩js
gulp.task('my-task-js', function(){
gulp.src('./js/*.js')
.pipe(jshint()) //js检测
.pipe(uglify()) //js压缩
.pipe(concat('all.js')) //合并为all.js
.pipe(rename({suffix: '.min'})) // 重命名为all.mim.js
.pipe(gulp.dest('./dist/js')) //输出到/dist/js目录
.pipe(connect.reload()) // 更新页面
});
// ...
复制代码

gulp.task是 gulp 的 api 定义一个使用 Orchestrator 实现的任务(task) 如上我们定义了my-task-jsmy-task-csshtmlcleandefaultwatchserver等任务,其中:

  • my-task-js 是将 符合所提供的匹配模式的 js 进行检测(gulp-jshint)、压缩(gulp-uglify)、合并(gulp-concat)、重命名(gulp-rename)、输出(gulp.dest)到/dist/js 目录下;

  • my-task-css 是将 符合所提供的匹配模式的 sass 进行编译(gulp-sass)、压缩(gulp-uglify)、合并(gulp-concat)、重命名(gulp-rename)、输出(gulp.dest)到/dist/css 目录下;

  • html 是将 符合所提供的匹配模式的 html 进行监听,如果有变化则 connect.reload()

  • clean 是如果任务重新启动时 删除旧文件;

  • default gulp 默认启动的任务

  • watch gulp 的 api 监视文件,并且可以在文件发生改动时候做一些事情。它总会返回一个 EventEmitter 来发射(emit) change 事件。

  • server 依赖 gulp-connect 启动一个服务器

1
2
3
4
5
6
7
8
gulp.task('server', function() {
connect.server({
port: 8080, //指定端口号,在浏览器中输入localhost:8080就可以直接访问生成的html页面
root: './', //指定html文件起始的根目录
livereload: true //启动实时刷新功能(配合上边的connect.reload()方法同步使用)
});
});
复制代码

配置完 gulpfile.js 之后,我们给 js 和 css 及 html 加点东西:
首先 js/helloworld.js

1
2
3
// helloworld.js
console.log('hello world')
复制代码

css/index.scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// index.scss
// 变量测试
$fontColor: #red;
$backColor: aqua;
// 嵌套类测试
div {
p {
font-weight: bold;
font-size: 20px;
color: $fontColor;
}
}
div{
background: $backColor;
}
复制代码

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>gulp-study</title>
<link href=/dist/css/all.min.css rel=stylesheet>
</head>
<body>
<div id="firstDiv">
<p>我是gulp</p>
<p>hello world</p>
</div>
<p>我是p标签</p>
<p>我是p标签</p>
</body>
<script src="/dist/js/all.min.js"></script>
</html>
复制代码

运行 gulp

1
2
$ gulp
复制代码


浏览器效果:

接下来我们修改 helloworld.js 来看看是否能实时刷新 修改如下:

1
2
3
4
5
// helloworld.js
console.log('hello world');
let firstDiv = document.getElementById('firstDiv')
console.log(firstDiv)
复制代码

按保存之后,终端给我们报了一个错:

查看 js 发现我们用了 es6 语法的声明语句 但当前 gulp 无法处理 es6 语法,有问题解决问题,es6=>es5
解决方案: 安装 gulp-babel babel-core babel-preset-es2015

1
2
npm i  --save-dev  gulp-babel babel-core babel-preset-es2015
复制代码

gulpfile.js 修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ...
const babel = require('gulp-babel');
// ...
// 定义名为 "my-task" 的任务压缩js
gulp.task('my-task-js', function(){
gulp.src('./js/*.js')
.pipe(babel())
.pipe(jshint())
.pipe(uglify())
.pipe(concat('all.js'))
.pipe(rename({suffix: '.min'}))
.pipe(gulp.dest('./dist/js'))
.pipe(connect.reload())
});
// ...
复制代码

运行

1
2
$ gulp
复制代码

依然报上面的错;找了一些原因发现,虽然安装了相关依赖,却没有配置.babelrc 文件,即 babel 还没转化 es6
根目录添加.babelrc 文件

1
2
3
4
{
"presets": ["es2015"]
}
复制代码

重新运行:

1
2
$ gulp
复制代码


查看 dist 下的 js 文件

改变 helloworld.js 检查页面是否刷新

1
2
3
4
5
6
// helloworld.js
console.log('hello world');
let firstDiv = document.getElementById('firstDiv')
console.log(firstDiv)
firstDiv.style.backgroundColor = 'yellow';
复制代码

保存,页面的天空蓝换成你们喜欢的 yellow 颜色

修改 index.scss 查看是否会刷新页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// index.scss
// 变量测试
$fontColor: #red;
$backColor: aqua;
// 嵌套类测试
div {
p {
font-weight: bold;
font-size: 20px;
color: $fontColor;
}
}
div{
background: $backColor;
width: 400px;
height: 400px;
margin: 0 auto;
}
复制代码


最后修改 index.html 查看是否会刷新页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>gulp-study</title>
<link href=/dist/css/all.min.css rel=stylesheet>
</head>
<body>
<div id="firstDiv">
<p>我是gulp</p>
<p>hello world</p>
</div>
<div>
<p>我是真的皮</p>
</div>
</body>
<script src="/dist/js/all.min.js"></script>
</html>
复制代码

Webpack 与 Grunt、Gulp 的区别?

随着前端发展如日冲天,前端项目也越来越复杂,得益于 Nodejs 的发展,前端模块化、组件化、工程化也大势所趋。这些年 Grunt、Gulp 到 Webpack 随着工程化的发展都大行其道。
前端工程化的早期,主要是解决重复任务的问题。Grunt、Gulp 就是其中代表。比如: 压缩、编译 less、sass、地址添加 hash、替换等。
Grunt 官网中就说:
对于需要反复重复的任务,例如压缩(minification)、编译、单元测试、linting 等,完成大部分无聊的工作。
而如今的 Webpack 更像一套前端工程化解决方案。利用强大插件机制,解决前端静态资源依赖管理的问题。

Webpack 作者 Tobias 回复与 Grunt Gulp NPM 脚本的比较

Tobias: NPM 脚本对我而言足矣。实际上,说 webpack 是 Grunt/Gulp 的替代器并不完全准确。Grunt 和 Gulp 以及 NPM 脚本都是任务执行程序
Webpack 是**模块打包程序**。这两类程序的目标不一样。但 webpack 简化了必须“过度使用”Grunt 和 Gulp 和 NPM 脚本才能实现的 Web 开发任务也是事实。NPM 脚本才是 Grunt 和 Gulp 的替代品。
不过,除了纯粹的构建之外,任务运行程序也有存在的理由,比如部署、代码检查、版本管理,等等。

Webpack 与 Grunt、Gulp 运行机制

1
2
3
4
5
6
# grunt gulp 思路
【遍历源文件】->【匹配规则】->【打包】
做不到按需加载,对打包的资源,是否用到,打包过程不关心。
# webpack
【入口】->【模块依赖加载】->【依赖分析】->【打包】
在加载、分析、打包的过程中,可以针对性的做一些解决方案。比如:code split(拆分公共代码)

Grunt 与 Gulp 性能比较

Grunt: 每个任务处理完成后存放在本地磁盘.tmp 目录中,有本地磁盘的 I/O 操作,会导致打包速度比较慢。
**Gulp: **gulp 与 grunt 都是按任务执行,gulp 有一个文件流的概念。每一步构建的结果并不会存在本地磁盘,而是保存在内存中,下一个步骤是可以使用上一个步骤的内存,大大增加了打包的速度。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!