最近花一点时间学了下 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. NIO学习:buffer读入与写出(文件复制示例)

    FileInputStream fInputStream=new FileInputStream(new File("/root/Desktop/testImage.jpg")); ...

  2. javascript笔记09:javascript的下拉式导航菜单

    <!DOCTYPE html> <html> <body> <script> function hideAll() { for(i=0;i<odi ...

  3. js按值传递还是按引用传递?

    js和其他大部分语言一样,有基本类型和引用类型.因此访问变量就有按值和按引用两种方式,但是传参的时候却只能按值传递.基本类型作为参数时按值传递自然无可厚非,但引用类型作为参数也按值传递就让人有点困惑了 ...

  4. 100 high quality blogs from java developers

    This list collects 100 high quality blogs from Java developers from all over the world. Some of thes ...

  5. centos6.5 apache+tomcat结合 负载均衡

    一. 负载均衡:根据 uri 表达式把请求分发给各个服务器处理.如:/*.jsp = lb_s  就是说把所有jsp请求交给lb_s , 当然它是个Tomcat. 二. 安装配置mod_jk    m ...

  6. Dev ComboxTree的实现

    一个项目中刚好用到,自己做下笔记备份 顺便和大家共享下 需要用到ASPxDropDownEdit ASPxTreeView 这两个控件 ASPxDropDownEdit对应的dll为:DevExpre ...

  7. 封装一个ISortable接口

    using System;/// <summary>/// 排序规范/// </summary>/// <typeparam name="T"> ...

  8. cognos10.2.2使用ODBC连接oracle92数据库(BMT-IMP-0016)

    对于Cognos Server 64位Windows系统,注意!请下载32位程序.即:   因为Cognos只认32位ODBC程序.这里千万要注意.对于64位的Windows系统的ODBC有两个程序, ...

  9. iOS开发——音频篇——音效的播放

    一.简单介绍 简单来说,音频可以分为2种 (1)音效 又称“短音频”,通常在程序中的播放时长为1~2秒 在应用程序中起到点缀效果,提升整体用户体验 (2)音乐 比如游戏中的“背景音乐”,一般播放时间较 ...

  10. asp.net:用类来后台绑定数据源

    //封装成一个 using System;using System.Collections.Generic;using System.Linq;using System.Web;using Syste ...