---
title: 使用 gulp 对资源进行压缩
date: 2023-08-24
modified: 2024-10-25
url: https://yuuu.org/post/gulp-compression
categories: ["折腾"]
tags: ["折腾"]
description: "在 Web 前端开发工作中有很多“重复工作”，比如压缩 CSS/JS 文件。而这些工作都是有规律的。找到这些规律，并编写 gulp 配置代码，让 gulp 自动执行这些“重复工作”。-- from gulp-book"
---
**2024/10/25：gulp 已经更新了 5.0，我所使用的配置文件中所用的 gulp-fontmin 是在目前为止是过时的插件，除非安装 gulp4，不然以下配置文件大概率会出现不可预料的错误。**

**2026/6/3：鉴于现在前端生态早已经发生了很大变化，Gulp 已经不再是大多数项目的首选构建工具。对于 Astro、Vite、Next.js、Nuxt 等现代框架而言，资源压缩、代码打包、热更新等功能通常已经开箱即用，因此新项目一般无需专门引入 Gulp。不过对于维护旧项目、静态网站或需要执行特定自动化任务的场景，Gulp 仍然是一个简单且实用的工具。本文内容仅作为记录和参考。**

在 Web 前端开发工作中有很多“重复工作”，比如压缩 CSS/JS 文件。而这些工作都是有规律的。找到这些规律，并编写 gulp 配置代码，让 gulp 自动执行这些“重复工作”。-- from 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 后自动安装。
安装完成后看看是否安装成功。

```bash
# 检查安装
node -v
npm -v
```

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

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

```bash
npm install -g gulp-cli
# 检查版本
gulp -v
```

## 安装插件

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

```bash
# 安装 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`，并且输入以下内容

```javascript
// 引入所需的插件
const gulp = require('gulp')
const clean = require('gulp-clean')
const cleanCSS = require('gulp-clean-css')
const fontmin = require('gulp-fontmin')
const htmlmin = require('gulp-html-minifier-terser')
const htmlclean = require('gulp-htmlclean')
const terser = require('gulp-terser')
const ttf2woff2 = require('gulp-ttf2woff2')

// 压缩和清理 HTML
gulp.task('minify-html', () => {
  return gulp.src('./public/**/*.html')// 看情况修改
    .pipe(htmlclean())
    .pipe(htmlmin({
      collapseWhitespace: true, // 折叠文档树中构成文本节点的空白区域
      removeComments: 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"
      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) => {
  const buffers = []
  gulp
    .src(['./public/**/*.html']) // 看情况修改
    .on('data', (file) => {
      buffers.push(file.contents)
    })
    .on('end', () => {
      const text = Buffer.concat(buffers).toString('utf-8')
      minifyFont(text, cb)
    })
})

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

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

// 删除字体原文件，主题生成的图片文件
gulp.task('clean-files', () => {
  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 压缩后的效果了。

```bash
hexo cl
hexo g
gulp
hexo s
```

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

![1](https://cdn.yuuu.org/post-img/9/1.webp)
![2](https://cdn.yuuu.org/post-img/9/2.webp)

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

```js
gulp.task('clean-files', () => {
  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](https://cdn.yuuu.org/post-img/9/3.webp)

## 注意事项

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

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

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

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

## 参考文章

[gulp-book](https://github.com/nimoc/gulp-book)
[使用 gulp 压缩博客静态资源](https://akilar.top/posts/49b73b87/)
