使用 Gulp 优化 Hexo 站点静态资源

2018年12月更:本教程仅适用于 gulp v3.x,升级 gulp4 的小伙伴可能要根据新的 api 修改下。
今天打开 Hexo 自动生成的 public 文件夹,惊奇地发现里面的 js , css 以及 html 文件竟然没有压缩,这就忍不了了。虽然文件数量不多,但是自动化的理念还是要灌输下去的。首先理一下需求:

  1. 这些文件没有统一一个入口文件,或者说实现起来麻烦,所以 webpack 以及 parcel 被pass掉。
  2. 需要有处理相应类型文件的模块。
  3. 需要有自动化流程。
  4. 得简单好用。

经过 237234871981 纳秒的思考以及 google 后,我决定使用 gulp 这个自动化构建工具。话不多说,开始我们的资源优化(压缩)之旅。

安装 gulp

gulp 可以通过 npm 安装,非常方便。

1
2
3
4
5
6
7
8
9
# 全局安装 gulp-cli
npm install -g gulp-cli

# 进入 hexo 项目,安装 gulp
npm install -D gulp

# 像 webpack 有一个配置文件一样
# gulp 的配置文件是 gulpfile.js
touch gulpfile.js

安装所需要的模块

gulp 提供了许多有用的模块,在压缩 css 和 js 这里我使用到 gulp-minify-css 以及 gulp-uglify 两个 npm 包。

使用模块

gulp 主要通过 gulp.task() 来构建任务,在处理文件方面一般会以 gulp.src() 指定文件的入口,以 gulp.dest() 指定输出文件的目录,然后通过 gulp.pipe() 来使用模块,使用过 koa 的同学会觉得这跟中间件的作用比较类似,用起来也会比较舒服。

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
// 引入模块
const gulp = require('gulp');
const minifycss = require('gulp-minify-css');
const uglify = require('gulp-uglify');

// 需要压缩的文件入口以及 output
// 这里以 hexo 生成的 public 文件夹为例子
const paths = {
styles: {
src: 'public/css/*.css',
dest: 'public/css/',
},
scripts: {
src: 'public/js/*.js',
dest: 'public/js/',
},
}

// 构建压缩 css 任务
gulp.task('minify-css', () => {
return gulp.src(paths.styles.src)
.pipe(minifycss())
.pipe(gulp.dest(paths.styles.dest));
});

// 构建压缩 js 任务
gulp.task('minify-js', () => {
return gulp.src(paths.scripts.src)
.pipe(uglify())
.pipe(gulp.dest(paths.scripts.dest));
});

以上就构建了两个任务,一个是压缩 css 并输出到源文件夹,另一个是压缩 js 并输出到源文件夹。

构建 default 任务

以上任务构建完了,但你运行 gulp 命令时并不会成功,因为需要添加默认任务。

gulpfile.js
1
2
3
4
5
6
7
8
9
10
11
12
13
// 构建任务队列
const minify_tasks = [
'minify-css',
'minify-js',
];

// 任务运行函数
const minify = () => {
gulp.run(minify_tasks);
};

// 构建默认任务
gulp.task('default', minify)

这样就构建好了一个默认任务,它是执行 gulp 命令时立即执行的任务,以上代码构建的默认任务开始时会运行所有的 minify 任务,压缩资源文件。现在就可以执行 gulp 压缩资源文件啦~

压缩 html

为什么要把压缩 html 文件分出来单独讲呢,因为我突然想起我的服务器开启了 gzip 压缩,再压缩 html 文件并没有多大提升。但是这里可以给一下教程给需要的同学。
gulp 提供了 gulp-htmlmin 模块用于压缩 html 文件。大家可以到 html-minifier 上查看更详细的配置项。

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
const gulp = require('gulp');
const htmlmin = require('gulp-htmlmin');

// html 文件目录
const paths = {
html: {
src: 'public/**/*.html',
dest: 'public/'
}
}

// 构建压缩 html 任务
gulp.task('minify-html', () => {

// html 压缩配置项
const htmlmin_options = {
removeComments: true, // 清除HTML注释
collapseWhitespace: true, // 压缩HTML
collapseBooleanAttributes: true, // 省略布尔属性的值 <input checked="true"/> ==> <input />
removeEmptyAttributes: true, // 删除所有空格作属性值 <input id="" /> ==> <input />
removeScriptTypeAttributes: true, // 删除<script>的type="text/javascript"
removeStyleLinkTypeAttributes: true, // 删除<style>和<link>的type="text/css"
minifyJS: true, // 压缩页面JS
minifyCSS: true // 压缩页面CSS
};

return gulp.src(paths.html.src)
.pipe(htmlmin(htmlmin_options))
});

然后问题就出现了,html 文件在多个不同层级的文件夹内,怎么把 html 文件压缩好后放回源文件夹呢?
经过 google 了解到 gulp-rename 这个 npm 包可以实现这样的效果,额外的使用方法可以到 这里 查看。以上需求的实现如下:

gulpfile.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 构建压缩 html 任务
const rename = require('gulp-rename');

// html 文件目录
// ...

gulp.task('minify-html', () => {

// html 压缩配置项
//...

return gulp.src(paths.html.src)
.pipe(htmlmin(htmlmin_options))
.pipe(rename({}))
.pipe(gulp.dest(paths.html.dest));
});

然后添加到 minify 任务列表即可:

gulpfile.js
1
2
3
4
5
6
// 构建任务队列
const minify_tasks = [
'minify-css',
'minify-js',
'minify-html',
];

这样运行 gulp 就会帮你处理需要处理的资源文件了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 开始 gulp
gulp

# log
[19:45:59] Using gulpfile your-project-path/gulpfile.js
[19:45:59] Starting 'default'...
gulp.run() has been deprecated. Use task dependencies orgulp.watch task triggering instead.
[19:45:59] Starting 'minify-css'...
[19:45:59] Starting 'minify-js'...
[19:45:59] Starting 'minify-html'...
[19:45:59] Finished 'default' after 13 ms
[19:46:02] Finished 'minify-css' after 2.5 s
[19:46:02] Finished 'minify-js' after 2.54 s
[19:46:02] Finished 'minify-html' after 2.63 s

到此我们的资源压缩之旅也结束啦~