blog icon indicating copy to clipboard operation
blog copied to clipboard

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

Open lin-xin opened this issue 7 years ago • 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>

lin-xin avatar Mar 09 '17 10:03 lin-xin

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

mercer08 avatar Apr 06 '17 09:04 mercer08

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

lin-xin avatar Apr 06 '17 09:04 lin-xin

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

vincent-cong avatar Jul 10 '17 08:07 vincent-cong

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

lin-xin avatar Jul 10 '17 08:07 lin-xin

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

vincent-cong avatar Jul 10 '17 08:07 vincent-cong

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

lin-xin avatar Jul 10 '17 08:07 lin-xin

@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 avatar Jul 10 '17 08:07 vincent-cong

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

lin-xin avatar Jul 10 '17 08:07 lin-xin

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

vincent-cong avatar Jul 10 '17 08:07 vincent-cong

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

dumuchenglin123 avatar Jul 26 '17 02:07 dumuchenglin123

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

lin-xin avatar Jul 26 '17 09:07 lin-xin

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

MagicHacker avatar Oct 10 '17 06:10 MagicHacker

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

lin-xin avatar Oct 10 '17 06:10 lin-xin

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

MagicHacker avatar Oct 10 '17 07:10 MagicHacker

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 avatar Oct 10 '17 10:10 Pulset

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

lin-xin avatar Oct 11 '17 00:10 lin-xin

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

Pulset avatar Oct 11 '17 01:10 Pulset

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

lin-xin avatar Oct 11 '17 02:10 lin-xin

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

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

Pulset avatar Oct 11 '17 09:10 Pulset

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

BiYuqi avatar Oct 15 '17 15:10 BiYuqi

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

lin-xin avatar Oct 16 '17 00:10 lin-xin

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

Yibangyu avatar Dec 24 '18 08:12 Yibangyu