Gulp 构建工具

项目构建是指项目上线之前对项目源代码进行一系列处理,使其以最佳的形式运行于线上服务器。常见处理任包括以下几方面:

1、模块化开发可以实现功能的复用并解决模块间的依赖关系,但带来好处的同时也使得功能代码的碎片化(若干文件)程度增加。

2、使用less、sass等预处理器,可以降低CSS的维护成本,最终需要将这些预处理器编译成css文件;

3、对静态资源(css、js、html、images)压缩合并可以提升网页打开速度,提高性能;

以上任务完如果完全靠手动来完成是非常耗时耗力的且容易出错,实际开发通常借助构建工具来实现。

所谓构建工具是指通过一系简单配置就可以帮我们实现合并、压缩、校验、预处理等一系列任务的软件工具。

常见的构建工具包括:Grunt、Gulp、F.I.S(百度出品)、webpack等。

Gulp是基于 Nodejs 开发的一个构建工具,借助 gulp 插件可以实现不同的构建任务,其以简洁的配置和卓越的性能成为目前主流的构建工具。

全局安装

1
$ npm install -g gulp

Gulp基础

1、本地安装 gulp

进入项目根目录执行

1
$ npm install gulp --save-dev

2、全局安装

1
$ npm install -g gulp

添加–save-dev会在package.json记录依赖关系.

3、任务清单

在项目根目录中创建 gulpfile.js,gulp会参考这个配置文件执行构建任务。

4、定义任务

gulpfile.js 定义构建任务,如压缩、合并等。

gulp 是通过调用插件来完成具体构建任务的,并且这些插件也都基于 Nodejs.

以编译 LESS 为例,安装

1
$ npm install gulp-less

使用 gulp, 定义了一个名称为 less 的任务,用来完成 less 编译成 css 的任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
var gulp = require('gulp');
var less = require('gulp-less');
// 定义任务 参数[名称,回调函数]
gulp.task('less', function(){
// 获取想要转换的路径,相对路径
// 用来找到要构建的资源 参数[资源路径]
gulp.src('./public/less/*.less')
// 将资源传给插件 参数[插件调用]
.pipe(less())
// 存放路径 将构建的好的资源重新存储 参数[路径]
.pipe(gulp.dest('./release/css'));
;
});

5、执行任务

打开命令行窗口并切换到项目根目录下,执行命令 gulp less,这时全局安装的 gulp 便以我们定义好的 gulpfile.js 执行构建任务了。

这样LESS文件便会编译成CSS文件,并保存在了./public/css目录下。

各种插件的使用:

Gulp工作原理

通过不同的插件实现构建任务,Gulp只是按着配置文件调用执行了这些插件。

Gulp API

Gulp是基于NodeJS的,通过require可以引入一个NodeJS的包(模块),其作用类似于浏览器中的script标签引入资源,被引入的包存放在node_modules目录下。

引入gulp包(模块)后返回一个对象(习惯赋值给变量gulp),通过该对象提供的方法(API)完成任务的配置。

1、gulp.task()

定义各种不同的任务,不同任务间存在依赖关系时,可以指定依赖。

2、gulp.src()

需要构建资源的路径,字符串或数组(可以正则方式书写)

2、gulp.pipe()

管道,将需要构建的资源“输送”给插件。

3、gulp.dest()

构建任务完成后资源存放的路径(会自动创建)

4、gulp.watch()

通过监视某静态资源的修改,然后可以调用相应任务。

常用Gulp插件

  • gulp-less
    • 编译LESS文件
  • gulp-autoprefixer
    • 添加CSS私有前缀
  • gulp-cssmin
    • 压缩CSS
  • gulp-rname
    • 重命名
  • gulp-imagemin
    • 图片压缩
  • gulp-uglify
    • 压缩Javascript
  • gulp-concat
    • 合并
  • gulp-htmlmin
    • 压缩HTML
  • gulp-rev
    • 添加版本号
  • gulp-rev-collector
    • 内容替换
  • gulp-useref
  • gulp-if
  • gulp-seajs-transport

例子

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// 引包
// 在 node-modules 下找
var gulp = require('gulp'),
// 删除文件的
clean = require('gulp-clean'),
// 添加 idleading
transport = require('gulp-seajs-transport'),
concat = require('gulp-seajs-concat'),
less = require('gulp-less'),
cssmin = require('gulp-cssmin'),
autoprefixer = require('gulp-autoprefixer'),
imagemin = require('gulp-imagemin'),
uglify = require('gulp-uglify'),
concat = require('gulp-concat'),
htmlmin = require('gulp-htmlmin'),
rev = require('gulp-rev'),
revCollector = require('gulp-rev-collector'),
useref = require('gulp-useref'),
gulpif = require('gulp-if'),
rename = require('gulp-rename'),
base64 = require('gulp-base64');
gulp.task('clean', function() {
// 路径相对于 gulpfile,跟 node 没关系
return gulp.src('./public/dist')
.pipe(clean());
});
// 添加 idleading
gulp.task('transport', ['clean'], function() {
gulp.src('./public/!(libs)/**/*.js')
.pipe(transport({
idleading: './public/dist/'
}))
.pipe(concat())
.pipe(uglify())
.pipe(gulp.dest('./public/dist'))
});
// css 任务
gulp.task('less2css', function(){
return gulp.src('./public/less/*.less')
.pipe(less())
.pipe(cssmin())
// 处理 CSS 中的图片为 base64
.pipe(base64())
.pipe(autoprefixer())
// 资源后缀改了,文件里的路径也需要改变,利用 gulp-rev-collector
// 先改名并存储,然后再替换
.pipe(rev())
.pipe(gulp.dest('./release/css'))
.pipe(rev.manifest())
.pipe(rename('css-mainfest.json'))
// 收集替换前后的关系
.pipe(gulp.dest('./release/rev'));
});
// 图片任务
// 构建过程中需要保证图片路是不变的
// src 有第二个参数,是一个对象
// 在 images 下面还有目录,那么这样还有问题
// 在加一个*,表示有多少层目录,都给我能找到
gulp.task('image', function(){
return gulp.src(['./images/**/*', './uploads/**/*'], {base: './'})
.pipe(imagemin())
.pipe(rev())
.pipe(gulp.dest('./release'))
.pipe(rev.manifest())
.pipe(rename('image-mainfest.json'))
.pipe(gulp.dest('./release/images'));
});
// js 资源
gulp.task('js', function(){
gulp.src('./libs/*.js')
.pipe(uglify())
// 合并,需要传参数,合并后的名称
.pipe(concat('all.js'))
.pipe(gulp.dest('./release/libs'));
});
// html 资源
gulp.task('html', function(){
gulp.src('./view/*.html')
// 需要参数,去掉空间,注释,压缩 js 代码
.pipe(htmlmin({
collapseWhitespace:true,
removeComments:true,
minifyJs: true
}))
.pipe(gulp.dest('./release/views'));
});
// 替换任务
gulp.task('revCollector', function(){
// 按照哪个标准去替换 两个参数
// 文件
// 替换哪个文件
gulp.src(['./release/rev/*.json', './release/*.html'])
.pipe(revCollector())
.pipe(gulp.dest('./release'));
});
// 页面中还引入了 jQuery,想要让两个文件做一个合并,前面的 gulp-concat 插件有点问题,还需要我们手动修改路径。
// 压缩合并处理路径任务,md5
gulp.task('useref', function(){
return gulp.src('./index.html')
// 找到 build 标记的内容,合并并替换
.pipe(useref())
// 假如是以 js 结尾的,合并并压缩,那么再调用下一个插件
// uglify 只能压缩 js 文件,使用 gulp-if 插件过滤 js 文件
.pipe(gulpif('*.js', uglify()))
// 静态资源改名字
.pipe(gulpif('*.js', rev()))
.pipe(gulp.dest('./release'))
// 做一个收集,合并前后的文件名称变化
.pipe(rev.manifest())
// 为了避免替换
.pipe(rename('js-manifest.json'))
.pipe(gulp.dest('./release/rev'));
});
// 其它内容
gulp.task('other', function() {
gulp.src(['./api/*', './public/font/*'], {base: './'})
.pipe(gulp.dest('./release'));
});
// 路径替换
gulp.task('rev', ['css', 'image', 'useref'], function () {
gulp.src(['./release/rev/*.json', './release/**/*.?(html|css)'])
.pipe(revCollector())
.pipe(gulp.dest('./release'));
});
gulp.task('default', ['rev', 'other', 'html']);

seajs 的配置文件中加 vars 键修改引入 seajs 文件的路径

1
2
3
4
5
6
7
8
9
seajs.config({
base: './public/asserts',
alias: {
'jquery': 'jQuery/jquery'
},
vars: {
path: 'dist'
}
});

重新配置生产环境(项目上线)文件路径:

1
2
3
4
<script>
// ./public/{path}/scripts/index 这个路径是模块名称需要的
seajs.use('./public/{path}/index');
</script>

bulid 的一个示例

1
2
3
4
<!-- build:js ./libs/all.js -->
<script src="./libs/jquery.min.js"></script>
<script src="./libs/toggle.js"></script>
<!-- endbuild -->

缓存、性能优化

<link href="base.css?v=1"> 后面的参数用来解决浏览器缓存

浏览器会将静态资源(CSS、images、js)缓存到本地浏览器中,下次请求时就读取本地资源来显示,缓存可以带来性能的提升,但是也会给前端开发者带来问题。往往这种问题是由缓存构成的。

怎么解决呢?分析浏览器缓存机制问题。什么情况下不需要浏览器缓存?

浏览器默认缓存按资源路径进行的缓存 ./css/base.css,因此每次开发加个参数 ./css/base.css?v=1 或者 ./css/base.css?v=20141123 加了一个时间戳;

前端性能优化:协商缓存,雅虎 13 条

当内容的确改变后才需要,就意味着我们需要知道内容是否改变了,利用 md5 可以处理。当更改后会形成新的,利用这个值充当文件名称。把文件加密成 md5 串,这种方式就是指纹

不同 js 文件合并

需要注释: ./libs/all.js 就是合并之后的名称

1
2
3
4
<!-- build:js ./libs/all.js -->
<script src='./libs/jquery.min.js'></script>
<script src='./libs/toggle.js'></script>
<!-- endbuild -->

移除一些上线不需要的文件。

1
2
3
4
5
6
7
8
9
10
<!-- 并没有实际合并,而是利用 build:css 来改名称 -->
<!-- build:css ./public/css/main.css -->
<link rel="stylesheet" href="./public/less/main.less">
<!-- endbuild -->
<!-- build:remove -->
<script src="./public/less/less.js"></script>
<!-- endbuild -->
<!-- build:remove -->
<script src='./libs/less.js'></script>
<!-- endbuild -->

合并之后压缩

.pipe(gulpif('*.js', uglify()))

gulp 构建过程总结

  • touch gulpfile.js
  • 全局安装 gulp
  • 本地安装 gulp
  • 处理 css
    • gulp-less
    • gup-cssmin
    • gup-autoprefixer
    • gulp-rev
    • rev.minifest() 改名称了
    • gulp-rename
  • 图片处理
    • gulp-imagemin
    • gulp-rename
  • 改名
    • gulp-useref
    • gulp-manifest
    • gulp-uglify
    • gulp-if
  • 路径替换
    • gulp-rev-collector
  • 压缩 html
    • gulp-htmlmin
  • 处理一些其它内容

监视文件变化

1
2
3
4
5
6
gulp.task('watch', function(){
// 可以用这个 watch 来实现实时编译的功能
gulp.watch('./index.html',['default'], function(event){
});
});

文件合并时需要对每一个模块娶一个名字

需要一个工具

seajs 在上线做合并处理的时候,并不是将所有文件合并才是最佳方式,
有一些公共第三方模块可以不做合并处理,利用浏览器缓存功能更会使得性能更好;

define(‘当前模块路径’, [‘依赖模块相对路径’], function() {});

要实现上述操作,需要在合并前

感谢您的支持!