最近花一点时间学了下 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. [React ] React Fundamentals: Component Lifecycle - Mounting Usage

    The previous lesson introduced the React component lifecycle mounting and unmounting. In this lesson ...

  2. Java中各种排序算法

    package org.rut.util.algorithm.support; import org.rut.util.algorithm.SortUtil; /** * @author treero ...

  3. 引用js或css后加?v= 版本号的用法(转)

    <span style="font-size:14px;">css和js带参数(形如.css?v=与.js?v= 或 .css?version=与.js?version ...

  4. Map的遍历方式

    public class Mapper { public static void main(String[] args) {  Map<String, String> map = new ...

  5. CentOS7上安装Pycharm

    下载pycharm $ wget https://download.jetbrains.com/python/pycharm-professional-2016.1.2.tar.gz 解压 $ .ta ...

  6. Sql面试常考题(持续添加)

    最近萌生换工作的念头,于是上网下载了一些公司的面试题,重新看了面试题中的Sql部分,这些查询题有时候只是兜一个弯角来考,对于给EF惯坏的孩子来说还是有点难度的(给面试官鄙视了几下的结果),所以列出最近 ...

  7. effective c++(03)之const使用方法

    char greeting[] = "hello"; char* p = greeting; //non-const pointer,non-const data const ch ...

  8. 推荐几款提高.net编程效率的辅助工具

    1.Resharper ReSharper是一个JetBrains公司出品的著名的代码生成工具,其能帮助Microsoft Visual Studio成为一个更佳的IDE.它包括一系列丰富的能大大增加 ...

  9. [转]SQL语句:Group By总结

    1. Group By 语句简介: Group By语句从英文的字面意义上理解就是“根据(by)一定的规则进行分组(Group)”.它的作用是通过一定的规则将一个数据集划分成若干个小的区域,然后针对若 ...

  10. IOS GCD 的理解

    GCD (Grand Central Dispatch) 是Apple公司开发的一种技术,它旨在优化多核环境中的并发操作并取代传统多线程的编程模式. 在Mac OS X 10.6和IOS 4.0之后开 ...