最近花一点时间学了下 gulp,顺便学了下 sass,因为工作中并不需要用(我比较希望学习是需求驱动),所以一直拖到现在才学。突然觉得学习这类工具性价比很高,半天一天即可上手,技能树丰富了(尽管可能只会 20%,但是可以完成 80% 的工作了啊!),简历丰富了,所以才有这么多 前端er 不屑数据结构和算法这些基础吧,毕竟投入产出比太低,学一个简单的算法的时间都够掌握两遍基本的 gulp 工作流了!

言归正传,今天要讲的是 gulp 的增量编译。在编译的过程中,有没有发现很多不必要的编译呢?

我们以如下的例子为例:

let dest = 'css/';

// sass -> css
gulp.task('css', function() {  // 任务名
   return gulp.src('sass/*.scss')  // 目标 sass 文件
    .pipe(plumber({errorHandler: notify.onError('Error: <%= error.message %>')}))
    .pipe(sass())  // sass -> css
    .pipe(debug({title: '编译'}))
    .pipe(gulp.dest(dest));
});

gulp.task('watch', function() {
  gulp.watch('sass/*.scss', ['css']);
});

gulp watch 启动监听,此时修改 sass 文件夹下的任意文件,都会编译该文件夹下的所有文件,这不是我们希望的,我们希望只对修改过的文件进行编译,即增量编译(Incremental Builds)。

gulp 官方推荐了 4 个用于增量编译的插件,我们一起来看看使用方法以及它们的差异。

  • gulp-changed - only pass through changed files
  • gulp-cached - in-memory file cache, not for operation on sets of files
  • gulp-remember - pairs nicely with gulp-cached
  • gulp-newer - pass through newer source files only, supports many:1 source:dest

gulp-changed

首先推荐的是 gulp-changed,毕竟它的作者在 GitHub 上拥有 15k 的 followers。

该插件默认是通过比较源文件和生成文件的修改时间来判断是否将文件往下传递(pipe 流),当然也可以通过 haschanged 改变默认比较方式,采用 sha1 比较,感觉有点像浏览器缓存中的 Last-Modified/If-Modified-Since 和 ETag/If-None-Match。个人觉得默认比较已经足够了。

let dest = 'css/';

// sass -> css
gulp.task('css', function() {  // 任务名
   return gulp.src('sass/*.scss')  // 目标 sass 文件
    .pipe(plumber({errorHandler: notify.onError('Error: <%= error.message %>')}))
    .pipe(changed(dest, {  // dest 参数需要和 gulp.dest 中的参数保持一致
      extension: '.css'  // 如果源文件和生成文件的后缀不同,这一行不能忘
    }))
    // sass() will only get the files that
    // changed since the last time it was run
    .pipe(sass())  // sass -> css
    .pipe(debug({title: '编译'}))
    .pipe(gulp.dest(dest))
});

gulp.task('watch', function() {
  gulp.watch('sass/*.scss', ['css']);
});

值得注意的是,如果源文件和生成文件的后缀不一样,需要加上 extension 参数。

个人认为还有两点需要注意。

第一点是因为 gulp-changed 是基于文件的判断,所以并不一定需要开启 watch(这和接下去要说的 gulp-cached 不同),怎么说?先用 gulp css 的命令编译一次,然后修改一个文件,再用 gulp css 编译一次,这样的操作是生效的。

第二点,因为 gulp-changed 只会将修改过的文件往下 pipe,所以如果后续有需要合并的操作(concat 操作),那么就会导致文件缺失,合并后的文件其实就是修改过的文件了。

所以 gulp-changed 只适合 1:1 的操作。

gulp-cached

和 gulp-changed 基于文件的对比不同,gulp-cached 可以将第一次传递给它的文件内容保留在内存中,如果之后再次执行 task,它会将传递给它的文件和内存中的文件进行比对,如果内容相同,就不再将该文件继续向后传递,从而做到了只对修改过的文件进行增量编译。

let dest = 'css/';

// sass -> css
gulp.task('css', function() {  // 任务名
   return gulp.src('sass/*.scss')  // 目标 sass 文件
    .pipe(plumber({errorHandler: notify.onError('Error: <%= error.message %>')}))
    .pipe(cached('sass-task'))  // 取个名字
    .pipe(sass())  // sass -> css
    .pipe(debug({title: '编译'}))
    .pipe(gulp.dest(dest))
});

gulp.task('watch', function() {
  gulp.watch('sass/*.scss', ['css']);
});

和 gulp-changed 不同的是,gulp-cached 必须要开启 gulp watch保证内存中存在副本,才能进行比较。

gulp-remember

gulp-remember 同样可以在内存中缓存所有曾经传递给它的文件,但是它和 gulp-cached 的区别是,在之后的 task 中,gulp-cached 会过滤掉未经修改的文件不再向下传递,而 gulp-remember 则会将未传递给它的文件进行补足从而能够继续向下传递,因此通过 gulp-cached 和 gulp-remember 的结合使用,既能做到只对修改过的文件进行编译,又能做到当相关联的文件任意一个发生改变时,编译所有相关的文件。所以我觉得实际开发中用 gulp-cached+gulp-remember 的组合非常合适。

let dest = 'css/';

// sass -> css
gulp.task('css', function() {  // 任务名
   return gulp.src('sass/*.scss')  // 目标 sass 文件
    .pipe(plumber({errorHandler: notify.onError('Error: <%= error.message %>')}))
    .pipe(cached('sass-task'))  // 取个名字
    .pipe(sass())  // sass -> css
    .pipe(debug({title: '编译'}))
    .pipe(gulp.dest(dest))
    .pipe(remember('sass-task'))  // 和 cached() 参数一致
    .pipe(concat('all.css'))
    .pipe(debug({title: '合并'}))
    .pipe(gulp.dest(dest))
});

gulp.task('watch', function() {
  gulp.watch('sass/*.scss', ['css']);
});

由于在第一次合并文件时,gulp-remember 已经将传递过来的文件缓存在内存中了,那么即使在后续的 task 执行中,gulp-cached 插件过滤掉了未经修改过的 css 文件,但是 gulp-remember 还是能够通过自己的缓存来补全这些缺失的文件,从而做到正确地合并文件。

我们还可以合理地管理两个插件的缓存,具体见文档。

gulp-newer

gulp-newer 和 gulp-changed 类似,也是基于文件对比,不过它只支持最后修改时间的对比。

1 : 1 进行增量编译的例子:

let dest = 'css/';

// sass -> css
gulp.task('css', function() {  // 任务名
   return gulp.src('sass/*.scss')  // 目标 sass 文件
    .pipe(plumber({errorHandler: notify.onError('Error: <%= error.message %>')}))
    .pipe(newer({
      dest: dest,
      ext: '.css'
    }))
    .pipe(sass())  // sass -> css
    .pipe(debug({title: '编译'}))
    .pipe(gulp.dest(dest))
});

gulp.task('watch', function() {
  gulp.watch('sass/*.scss', ['css']);
});

1 对 多进行增量编译的例子:

let dest = 'css/';

// sass -> css
gulp.task('css', function() {  // 任务名
   return gulp.src('sass/*.scss')  // 目标 sass 文件
    .pipe(plumber({errorHandler: notify.onError('Error: <%= error.message %>')}))
    .pipe(newer('dest' + 'all.css'))
    .pipe(sass())
    .pipe(concat('all.css'))
    .pipe(gulp.dest(dest));
});

gulp.task('watch', function() {
  gulp.watch('sass/*.scss', ['css']);
});

遗憾的是,貌似不能同时 1:1 & 1:多 进行编译(此处问号脸 ):)

gulp-watch

除了以上 4 个插件外,用 gulp-watch 也是可以的。

let dest = 'css/';

// sass -> css
gulp.task('css', function() {  // 任务名
   return gulp.src('sass/*.scss')  // 目标 sass 文件
    .pipe(plumber({errorHandler: notify.onError('Error: <%= error.message %>')}))
    .pipe(watch('sass/*.scss'))
    .pipe(sass())
    .pipe(debug({title: '编译'}))
    .pipe(gulp.dest(dest));
});

gulp.task('watch', function() {
  gulp.watch('sass/*.scss', ['css']);
});

gulp-watch 也不能使用类似 gulp-concat 的工具进行一对多的编译。


之前关于 gulp 的基础笔记都不敢往首页发,本文发到首页,除了觉得很多人使用 gulp 可能不关注增量编译外,还想知道大家在实际的开发中使用的增量编译插件方式是?个人觉得 gulp-cached+gulp-remember 的搭配不错,不过我还没在实际开发中用过 gulp,所以想听听老司机的意见。

参考:

gulp 中的增量编译的更多相关文章

  1. 如何在Gulp中提高Browserify的打包速度

    使用Browserify打包js时如果项目变得越来越大,编译时间就会相应变得越来越长.使用官方的插件watchify是个比较有效的提高速度方案. 提速原理 watchify的用法和gulp的watch ...

  2. gulp中pipe的作用和来源

    gulp的pipe方法是来自nodejs stream API的,并不是gulp本身源码所定义的. 一.pipe方法的作用 pipe跟他字面意思一样只是一个管道 例如我有一堆文件 var s = gu ...

  3. 在Gulp中使用BrowserSync

    博客已迁移至http://zlwis.me. 很早就听说过BrowserSync,也看过一些相关文章,可就是没用过.之前一直在用Gulp开发项目,每次编写完Sass后还要用按F5刷新页面看效果,想想也 ...

  4. 【前端】在Gulp中使用Babel

    Install $ npm install --save-dev gulp-babel babel-preset-es2015 用法1: const gulp = require('gulp'); c ...

  5. gulp中常用的模块

    gulp-cssmin:  css压缩 gulp-uglify: js压缩混淆 gulp-imagemin: 图片压缩 gulp-htmlmin: html压缩 gulp-concat: 文件合并 g ...

  6. gulp自己主动化任务脚本在HybridApp开发中的使用

    眼下做前端开发的同学可能都熟悉grunt.fis之类的自己主动化构建工具.事实上在HybridApp开发中我们也能够使用这些工具来简化我们的工作.gulp就是一个比grunt,fis都先进的构建工具. ...

  7. 在Visual Studio 2015的Cordova项目中使用Gulp

    之前一直是在vs 2013中使用Cordova来开发移动app(目前有iPad版/iPhone版/安卓版),准备到下一个milestone的时候升级到2015,这两天在尝试各种东西. 2015中的co ...

  8. gulp入门之常见处理方式(三)

    整合 streams 来处理错误 默认情况下,在 stream 中发生一个错误的话,它会被直接抛出,除非已经有一个时间监听器监听着 error时间. 这在处理一个比较长的管道操作的时候会显得比较棘手. ...

  9. 执行 $Gulp 时发生了什么 —— 基于 Gulp 的前端集成解决方案(二)

    前言 文章 在windows下安装gulp —— 基于 Gulp 的前端集成解决方案(一) 中,已经完成对 gulp 的安装,由于是window环境,文中特意提到了可以通过安装 gitbash 来代替 ...

随机推荐

  1. [Javascript] The Array filter method

    One very common operation in programming is to iterate through an Array's contents, apply a test fun ...

  2. java pdu短信解码

    java pdu短信解码 长短信未验证 有兴趣的可以试试 根据python的方法改写的 /** * PDU短信解析 * * * @param pduPayload * @return */ publi ...

  3. Android_ImageView

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools=&q ...

  4. rndc: connect failed: 127.0.0.1#953: connection refused

    [root@localhost sbin]# ./named -v bind 9.5.1-p3-v3.0.9 问题现象: [root@localhost sbin]# ./rndc flush -p ...

  5. Java中的编码问题

    下面将侧重介绍java乱码是如何产生的.存在哪些乱码的情况.该如何从根本上解决乱码问题.各位随博主一起征服令人厌烦的java乱码问题吧!!! 一.Java编码转换过程 我们总是用一个java类文件和用 ...

  6. unity3d最新面试题与参考答案汇总

    1.在类的构造函数前加上static会报什么错?为什么? 构造函数格式为 public+类名,如果加上static会报错(静态构造函数不能有访问修饰符)原因:静态构造函数不允许访问修饰符,也不接受任何 ...

  7. 20151221jquery学习笔记---日历UI

    妹的,这几天真是无语了,参加了一个无聊的比赛,简直浪费时间,好几天没学jquery啊,今天学了一点,不过快要期末考试了,估计得攒到寒假了啊. 日历(datepicker) UI, 可以让用户更加直观的 ...

  8. poj1182 食物链(种类并查集)详解

    poj 1182   http://poj.org/problem?id=1182 分析:这个题大意说的非常清楚了,就是求出假话的个数,题目中给的假话要求有三个 ① 当前的话与前面的某些真的话冲突,是 ...

  9. javascript 之封装技巧

    技术--- 闭包 javascript中的对象属性都是公开的,外界都可访问到,例: function cat(){ this.name = '猫'; this.climb = function(){ ...

  10. spring boot 配置文件提示自定义配置属性

    1.引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...