lin-xin/blog

2019年Gulp自动化压缩合并构建的解决方案

lin-xin opened this issue · 22 comments

虽然网上有很多的 gulp 构建文章,但是很多都已经随着 gulp 插件的更新无法运行了。因此,我写了这个比较简单的构建方案。本文基于 gulp 最新的 4.0.2 版本进行了修改。现在前端组件化项目大多是基于 webpack 进行构建,但是有一个零散的小业务,静态页面之类的,使用 gulp 反而会更加简单方便。

如果还不熟悉 gulp 的插件,可以阅读上一篇文章:精通gulp常用插件

这个方案主要是为了实现es6转es5、js/css的压缩合并、自动添加版本号和压缩html。

  • gulp-babel es6转es5
  • gulp-csso 压缩优化css
  • gulp-uglify 压缩js
  • gulp-htmlmin 压缩html
  • gulp-filter 过滤文件
  • gulp-rev-all 生成版本号

主要通过上面插件实现功能,其他插件配合使用。

安装相关依赖:npm i gulp gulp-uglify gulp-htmlmin gulp-useref gulp-csso gulp-filter gulp-rev-all gulp-base64 gulp-autoprefixer del gulp-babel @babel/core @babel/preset-env -D

// gulpfile.js
const { series, parallel, src, dest } = require('gulp');
const uglify = require('gulp-uglify');
const htmlmini = require('gulp-htmlmin');
const useref = require('gulp-useref');
const csso = require('gulp-csso');
const filter = require('gulp-filter');
const babel = require('gulp-babel');
const RevAll = require('gulp-rev-all');
const base64 = require('gulp-base64');
const autoprefixer = require('gulp-autoprefixer');
const del = require('del');

// 压缩html配置
const options = {
    removeComments: true,
    collapseWhitespace: true,
    collapseBooleanAttributes: true,
    removeEmptyAttributes: true,
    removeScriptTypeAttributes: true,
    removeStyleLinkTypeAttributes: true,
    minifyJS: true,
    minifyCSS: true
};

const defaultTask = cb => {
    const jsFilter = filter('**/*.js', { restore: true });
    const cssFilter = filter('**/*.css', { restore: true });
    const htmlFilter = filter(['**/*.html'], { restore: true });
    src('*.html')
        .pipe(useref())                     // 解析html中的构建块
        .pipe(jsFilter)                     // 过滤所有js
        .pipe(babel({
            presets: ['@babel/env'],
            sourceType: 'script'
        }))
        .pipe(uglify())                     // 压缩js
        .pipe(jsFilter.restore)
        .pipe(cssFilter)                    // 过滤所有css
        .pipe(autoprefixer())               // 添加css前缀
        .pipe(base64())
        .pipe(csso())                       // 压缩优化css
        .pipe(cssFilter.restore)
        .pipe(RevAll.revision({             // 生成版本号
            dontRenameFile: ['.html'],      // 不给 html 文件添加版本号
            dontUpdateReference: ['.html']  // 不给文件里链接的html加版本号
        }))
        .pipe(htmlFilter)                   // 过滤所有html
        .pipe(htmlmini(options))            // 压缩html
        .pipe(htmlFilter.restore)
        .pipe(dest('./dist'))
        .on('error', function (err) {
            throw new Error(err.toString())
        })
    cb();
}

const delDist = cb => {
    del.sync(['./dist']);
    cb();
}

const copyAssets = cb => {
    src('static/img/**').pipe(dest('./dist/static/img/'));
    cb();
}

exports.default = series(delDist, parallel(defaultTask, copyAssets));

在html中,我们需要先定义好构建块。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>gulp自动化构建解决方案</title>
    <!-- build:css static/css/index.css -->     // 定义了构建后引用的css路径
    <link rel="stylesheet" href="static/css/common.css"/>
    <link rel="stylesheet" href="static/css/index.css"/>
    <!-- endbuild -->
</head>
<body>
    ......
    
    <!-- build:js static/js/index.js -->        // 定义了构建后引用的js路径
    <script src="static/js/jquery.js"></script>
    <script src="static/js/common.js"></script>
    <script src="static/js/index.js"></script>
    <!-- endbuild -->
</body>
</html>

执行构建完成后,会生成 dist 文件夹,目录为:

|-dist
|   |-static
|       |-css
|           |-index.96cf44da.css
|       |-img
|       |-js
|           |-index.42ce3282.js
|   |-index.html

构建完的index.html,我们忽略压缩的html,完成了压缩合并添加版本号等功能。

// dist/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>gulp自动化构建解决方案</title>
    <link rel="stylesheet" href="static/css/index.96cf44da.css"/>
</head>
<body>
    ......

    <script src="static/js/index.42ce3282.js"></script>
</body>
</html>

对于图片这种静态资源发布到cdn的话,是不是也要生成md5指纹呢?

@2lei 是啊,不然的话你图片更改之后,在页面访问一直是访问到缓存的未改之前的图片

@lin-xin 你好 这个 gulpfile.js 可以完善一下吗?

@vincent-cong 这个哪里有问题吗?

@lin-xin 打包完了 并没有dist文件夹。。。。这个是什么原因?这个你可以出一个案列吗?非常感谢。

@vincent-cong 像上面涉及到的路径,你要按照你实际项目的路径修改一下

@lin-xin
var jsFilter = filter('src/js/.js',{restore:true}),
cssFilter = filter('src/js/
.css',{restore:true}),
htmlFilter = filter(['src/.html'],{restore:true});
gulp.src('src/
.html')
加过了,也没用。。。你可以自己试试看

我的文件目录是 src --js --aa.js src --css-aa.css src --aa.html
*.js *.css *.html 星号都是有的

@vincent-cong 我没有你的文件我怎么试呢,我这边是没问题的哦

@lin-xin 好吧 你那是好的,那估计是我的目录不对。。。我再按照你上面的目录弄一下吧,灰常感谢。如果方便的话可以加个例子么?

怎么html压缩 语句不起作用啊?
image

@dumuchenglin123 压缩的代码你注释掉了。用了 gulpif( ) 你的condition ,你有定义吗?condition 为true时才会执行后面的压缩。

@lin-xin 为什么要进行文件过滤呢?

@MagicHacker
因为 gulp.src('/*.html') 源文件是针对 html 的,所以过滤出这个html里面引用的js和css进行操作

@lin-xin ok,明白了,谢谢。没注意到路径是/*.html的。我刚用这个东西不久,平时都是单独定义任务的。

gulp.task('test', function () {
    var jsFilter = filter(['src/js/**/*.js', '!src/js/libs/**/*.js'],{restore:true}),
        cssFilter = filter('src/css/**/*.css',{restore:true}),
        htmlFilter = filter(['src/html/**/*.html'],{restore:true});
    gulp.src('src/html/**/*.html')
        .pipe(useref())                         // 解析html中的构建块
        .pipe(jsFilter)                         // 过滤所有js
        .pipe(uglify())                         // 压缩js
        .pipe(jsFilter.restore)
        .pipe(cssFilter)                        // 过滤所有css
        .pipe(minifycss())                           // 压缩优化css
        .pipe(cssFilter.restore)
        .pipe(RevAll.revision({                 // 生成版本号
            dontRenameFile: ['.html'],          // 不给 html 文件添加版本号
            dontUpdateReference: ['.html']      // 不给文件里链接的html加版本号
        }))
        .pipe(htmlFilter)                       // 过滤所有html
        .pipe(htmlmini())                       // 压缩html
        .pipe(htmlFilter.restore)
        .pipe(gulp.dest('dist/'))
})

为什么我过滤所有js之后,后面进行压缩,好像没有执行这个操作?大神,帮我看看哪里写错了

@Pulset html里有没有加上 < ! -- build:css xxx --> < ! -- endbuild -->

@lin-xin 加上了,js、css也能合并之后输出,但是都没有压缩

@Pulset 那是不是忘了安装 gulp-uglify 等压缩插件还是引入的出了问题

@lin-xin 原来是路径问题,改成这样就行了

var jsFilter = filter('**/*.js', { restore: true }),
  cssFilter = filter('**/*.css', { restore: true }),
  htmlFilter = filter('**/*.htm', { restore: true });

请问博主,针对多页面有什么好的解决方案吗,除了index.html 我还有htm/**/*.html等页面 自动化压缩打版本加hash自动替换,有什么好的解决方法吗

@BiYuqi 多页面也适合用这个方案的

那页面中的图片该怎么处理勒?