使用 gulp 对资源进行压缩

在 Web 前端开发工作中有很多 “重复工作”,比如压缩 CSS/JS 文件。而这些工作都是有规律的。找到这些规律,并编写 gulp 配置代码,让 gulp 自动执行这些 “重复工作”。– form gulp-book

什么是 gulp

Gulp 是一个流行的自动化构建工具,用于简化这些重复性的任务。Gulp 的核心思想是利用代码来自动化处理开发过程中的繁琐任务,从而提高开发效率和代码质量。
正如官网上所说:
“A toolkit to automate & enhance your workflow”
“Leverage gulp and the flexibility of JavaScript to automate slow, repetitive workflows and compose them into efficient build pipelines.”

而 Gulp 的强大之处在于它的插件生态系统。插件是用于处理特定任务的 JavaScript 模块。我们可以安装部分插件来使我们完成对博客静态资源的压缩。

安装 gulp

gulp 是基于 node.js 开发的,我们需要先安装 node.js 和 npm,npm 会在安装完 node.js 后自动安装。
安装完成后看看是否安装成功。

1
2
3
# 检查安装
node -v
npm -v

我使用 hexo 来建站,hexo 本身需要依托 node.js,所以使用 hexo 的朋友可以跳过安装 node.js 这一步。

安装 Gulp 的命令行工具,这样可以使我们在命令行中使用 gulp。

1
2
3
npm install -g gulp-cli
# 检查版本
gulp -v

安装插件

gulp 拥有着丰富的插件支持,其中就有我们所需要的压缩网页的插件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 安装 gulp 
npm install gulp --save-dev
# 删除无用文件
npm install gulp-clean --save-dev
# 压缩和优化 CSS
npm install gulp-clean-css --save-dev
# 压缩字体文件
npm install gulp-fontmin --save-dev
# 压缩和优化 HTML 文件
npm install gulp-html-minifier-terser --save-dev
# 清理和格式化 HTML 文件
npm install gulp-htmlclean --save-dev
# 压缩和混淆 JavaScript 文件
npm install gulp-terser --save-dev
# 用于将 TrueType 字体文件 (.ttf) 转换为 WOFF2 格式
npm install gulp-ttf2woff2 --save-dev

使用

安装完成后,在博客根目录新建 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
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
// 引入所需的插件
const gulp = require('gulp');
const htmlmin = require('gulp-html-minifier-terser');
const htmlclean = require('gulp-htmlclean');
const cleanCSS = require('gulp-clean-css');
const terser = require('gulp-terser');
const fontmin = require('gulp-fontmin');
const ttf2woff2 = require('gulp-ttf2woff2');
const clean = require('gulp-clean');

// 压缩和清理 HTML
gulp.task('minify-html', () => {
return gulp.src('./public/**/*.html')// 看情况修改
.pipe(htmlclean())
.pipe(htmlmin({
collapseWhitespace: true ,
removeComments: true, //清除html注释
collapseWhitespace: true, //折叠文档树中构成文本节点的空白区域
collapseBooleanAttributes: true,
//省略布尔属性的值,例如:<input checked="true"/> ==> <input />
removeEmptyAttributes: true,
//删除所有空格作属性值,例如:<input id="" /> ==> <input />
removeScriptTypeAttributes: true,
//删除<script>的type="text/javascript"
removeStyleLinkTypeAttributes: true,
//删除<style>和<link>的 type="text/css"
removeAttributeQuotes: true, // 移除属性值周围的引号
removeCDATASectionsFromCDATA: true, // 从 CDATA 部分移除注释
caseSensitive: true, // 以区分大小写的方式处理属性
minifyJS: true, //压缩页面 JS
minifyCSS: true, //压缩页面 CSS
minifyURLs: true , //压缩页面URL
}))
.pipe(gulp.dest('./public'))// 看情况修改
});

// 压缩 CSS
gulp.task('minify-css', () => {
return gulp.src(['./public/**/*.css'])// 看情况修改
.pipe(cleanCSS({
level: {
2: {
mergeAdjacentRules: true,
// 控制相邻规则的合并,默认为 true
mergeIntoShorthands: true,
// 控制属性合并为简写形式,默认为 true
mergeMedia: true,
// 控制 @media 规则的合并,默认为 true
mergeNonAdjacentRules: true,
// 控制非相邻规则的合并,默认为 true
mergeSemantically: false,
// 控制语义合并,默认为 false
overrideProperties: true,
// 基于可读性控制属性覆盖,默认为 true
removeEmpty: true,
// 控制删除空规则和嵌套块,默认为 true
reduceNonAdjacentRules: true,
// 控制非相邻规则的缩减,默认为 true
removeDuplicateFontRules: true,
// 控制删除重复的 @font-face 规则,默认为 true
removeDuplicateMediaBlocks: true,
// 控制删除重复的 @media 规则,默认为 true
removeDuplicateRules: true,
// 控制删除重复的规则,默认为 true
removeUnusedAtRules: false,
// 控制删除未使用的 at 规则,默认为 false(自版本 4.1.0 起可用)
restructureRules: false,
// 控制规则重构,默认为 false
skipProperties: []
// 控制不进行优化的属性,默认为 [],即所有属性都进行优化(自版本 4.1.0 起可用)
}
}
}))
.pipe(gulp.dest('./public'));// 看情况修改
});

// 压缩 JavaScript
gulp.task('compress', async() =>{
return gulp.src(['./public/**/*.js', '!./public/**/*.min.js'])// 看情况修改
.pipe(terser())
.pipe(gulp.dest('./public'));// 看情况修改
});

//读取文章使用字体,然后压缩
gulp.task('mini-font', (cb) => {
var buffers = [];
gulp
.src(['./public/**/*.html']) // 看情况修改
.on('data', function(file) {
buffers.push(file.contents);
})
.on('end', function() {
var text = Buffer.concat(buffers).toString('utf-8');
minifyFont(text, cb);
});
});

//压缩字体
function minifyFont(text, cb) {
gulp
.src('./public/font/*.ttf') //原字体所在目录
.pipe(fontmin({
text: text
}))
.pipe(gulp.dest('./public/fonts/')) //压缩后的输出目录
.on('end', cb);
}

// 定义转换 TTF 到 WOFF2 的任务
gulp.task('convertFonts', function() {
return gulp.src('./public/fonts/*.ttf')// 看情况修改
.pipe(ttf2woff2())
.pipe(gulp.dest('./public/fonts/'));// 看情况修改
});

//删除字体原文件,主题生成的图片文件
gulp.task('clean-files', function() {
return gulp.src(['./public/font','./public/images'], { read: false, allowEmpty: true })// 看情况修改
.pipe(clean());
});

// 运行gulp命令时依次执行以下任务
gulp.task('default', gulp.series(
gulp.parallel('compress', 'minify-css', 'minify-html', 'mini-font',),
'convertFonts',// 在 'mini-font' 完成后执行 'convertFonts',最后执行clean-files
'clean-files'
))

运行和小小的改进

重新渲染一次,并执行 gulp,接下来就可以看到 gulp 压缩后的效果了。

1
2
3
4
hexo cl
hexo g
gulp
hexo s

上图为 gulp 执行前后的文件大小对比图,其中 public 为未执行 gulp 指令,public-gulp 为执行 gulp 指令。
下图两者均去除字体文件和 images 文件夹后只对比 html,css,js 的大小。
1
2

可以看出,在去除字体文件和 images 文件夹后,其他的文件压缩了大约 100kb 的大小 o_o ….。但是在网站中占大头的字体文件却是狠狠的压缩了,总算是不用担心网站打开速度被字体拖慢了。
尽管从上图看,字体的压缩看似是从 15MB 降低至了 8MB,但其实在 tff2woff2 所转化的字体文件夹中,会同时将 tff 转化为 .eot , .woff2 , .woff , .ttf , .svg 多个字体格式,从而做到各个网站的适配。而目前主流浏览器大多使用 woff2 格式,也就是说,我们打开网页的时候,大概率是使用 woff2 字体,如果不去适配 IE 和低版本浏览器,大概也可以再将字体文件删除。下边的代码块是只保留 woff2 字体删除其他字体的。虽然没什么用,毕竟浏览器是按照样式表来下载字体的,多余的字体文件不会被下载,留在这里也没什么影响。

1
2
3
4
5
6
7
8
9
10
11
12

gulp.task('clean-files', function() {
return gulp.src([
'./public/font',
'./public/images',
'./public/fonts/yourfont.eot',
'./public/fonts/yourfont.ttf',
'./public/fonts/yourfont.svg',
'./public/fonts/yourfont.woff',// 看情况修改字体名字
], { read: false, allowEmpty: true })
.pipe(clean());
});

附上一张不使用任何处理,只是用 ttf2woff2 处理字体后,输出文件夹字体占用大小截图,可以看到,svg 居然占到了丧心病狂的 4M,而 woff2 只有 699kb,删了会清爽很多

3

注意事项

ttf2woff2 会在指定目录生成一个文件夹,其中会有’字体文件名字.css’的 css 文件,将它引入即可使用转化后的字体文件。

使用 gulp 压缩字体必须在本地进行,进一步导致本地文件增大,看着有点难受。我有在想,是否可以做到当博客仓库提交的时候自动触发 action,拉取字体文件进行处理,然后博客再通过网络引入该 css,从而做到 public 文件夹的干净。但是最后实在是不想折腾,也就放弃了。

我所使用的图片均由外链引入,不需要 hexo 的 images 文件夹,所以使用 clean-files 时顺便将其删除。如需使用 images,请自行修改。当然,gulp 也有与压缩图片有关的插件,可自行搜索使用。

更详细的设置请参考所用插件的官方文档。

参考文章

gulp-book
使用 gulp 压缩博客静态资源