点评君第10期Gulp 范儿——Gulp 高级技巧
Posted 腾讯FERD
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了点评君第10期Gulp 范儿——Gulp 高级技巧相关的知识,希望对你有一定的参考价值。
你会用gulp么?“会用啊,不就是创建一个任务然后执行就行了么”。too young,如果你是这么想的话,那么只是进入到gulp这座万丈高楼的地下室了而已。那如何才能在gulp的道路上更上一层楼呢,下面的文章将会告诉你。
Gulp 的语法相比 Grunt 要直观很多,配置一个基础任务完全是小菜一碟:
这种方式能够应付很多情形,但如果需要更多定制时,可能会遇到一些棘手的问题。本文将介绍一部分问题并提供解决方案。
使用 gulp 过程中,偶尔会遇到 Streaming not supported 这样的错误。这通常是因为常规流与 vinyl 文件对象流有差异、gulp 插件使用了只支持 buffer (不支持 stream)的库。
比如,不能把 Node 常规流直接传递给 gulp 及其插件。下面的代码试图使用 gulp-uglify 和 gulp-rename 转换一个读取流的内容,然后使用 gulp.dest() 写入结果:
显然,上面的代码会抛出错误。为什么不能把读取流传送给 gulp 插件呢?Gulp 不是传说中的streaming 构建工具吗?没错,但是上面的代码忽略了 gulp 预期输入的是 Vinyl 文件对象,直接输入读取流当然不行。
译者注:Gulp 管道中的「流」不是操作 Strings 和 Buffers 的常规 Node.js 流,而是启用 object mode 的流。Gulp 中的流发送的是 vinyl 文件对象。
Streams that are in object mode can emit generic javascript values other than Buffers and Strings.
Gulp 使用 vinyl-fs ,并从 vinyl-fs 继承了 gulp.src() 和 gulp.dest() 方法。而 vinyl-fs 使用了vinyl 文件对象,即 「虚拟文件格式」。要在 gulp 及其插件中使用常规读取流,需要把读取流转换为 vinyl 文件对象先。
vinyl-source-stream 便是一个转换工具:
下面的例子使用 [Browserify][] 打包,最后把结果转换成 vinyl 文件对象流。
搞定!vinyl-source-stream 使用指定的文件名创建了一个 vinyl 文件对象实例,因此可以不再使用 gulp-rename(gulp.dest 将用此文件名写入结果)。
gulp.dest() 创建一个写入流,使用来自 src 的文件名创建文件,并根据需要使用 mkdirp创建文件夹。写入文件后,还可以继续传送 stream 进行其他操作(比如 gzip 数据然后写入其他文件)。
与常规流不同,gulp.src() 传输的流是把内容转换成 buffer 的 vinyl 文件对象,因此 gulp 中得到的不是数据块(chunk),而是包含 buffer 的虚拟文件。Vinyl 文件格式有一个 contents 属性来存放文件内容(buffer 或者 stream 或者 null)。
译者注:通过 vinyl 对象的以下方法可以判断 contents 属性存放内容的类型:
isBuffer(): Returns true if file.contents is a Buffer.
isStream(): Returns true if file.contents is a Stream.
isNull(): Returns true if file.contents is null.
上面的代码展示了整个文件发射给 stream 之前,数据先被转换成 buffer,存放在 vinyl 文件对象中。
虽然一般推荐 stream 化数据,但是许多 gulp 插件基于使用 buffer 的库开发。有时则是需要基于完整的源内容做转换,比如基于字符串的正则替换,分块的文件可能会出现匹配遗漏的情况。同样地,诸如 UglifyJS 及 Traceur compiler 之类的工具也需要完整的文件输入(至少是 JavaScript 句法完整的字符串)。
因此 gulp 选择默认使用内容转换成 buffer 的 vinyl 对象流,以方便处理。
转换成 buffer 的缺点是对于大文件处理效率低下,因为文件发射回对象流之前必须完整读取。但对于常见的文本文件,如 JavaScript、CSS、模板等,使用 buffer 只是很小的花销,不会真正损害性能。
当然,设置 buffer: false 选项,可以让 gulp 禁用 buffer:
基于依赖的模块返回的是 stream, 以及 gulp 插件对 stream 的支持情况,有时需要把 stream 转换为 buffer(反之亦然)。如前面所言,很多插件只支持 buffer,如 gulp-uglify、gulp-traceur,使用时可以通过 gulp-buffer 转换:
也可以通过使用 gulp-streamify 或者 gulp-stream 插件,让只支持 buffer 的插件直接处理 stream。这样在基于 buffer 的插件前、后都可以使用基于 stream 的插件了。
尽管 gulp 有很多实用、方便的插件,但是一些任务不使用插件也能轻松完成。插件会增加额外的 NPM 依赖,插件接口、插件质量等也会产生一些维护花销。对于没有插件也能实现的任务,或者可以直接使用源模块的,建议不要使用插件。
理解上面的观点对针对实际情形做出正确的选择很重要。来看几个例子。
前面我们已经看到直接使用 Browserify 替代 gulp-browserify(已被 Gulp 官方拉黑)的例子,实现要点是使用 vinyl-source-stream (或类似功能的模块)把常规读取流转换为 vinyl 文件对象。
另一个例子是基于字符串的转换。下面的是一个基础的直接使用 vinyl 文件对象的插件:
上面的插件还不完善,没有处理 vinyl 文件对象包含 stream 的情形,但展示了实现转换任务其实很容易,只需要几行代码。through2 是优秀的 Node Stream 封装,使我们可以很方便的操作流。
如果需要执行自定义或者动态的任务,了解 gulp 所使用的 Orchestrator 模块会很有帮助。gulp.add 等同于 Orchestrator.add,实际上所有方法都继承自 Orchestrator 模块。下列情形可能会需要 Orchestrator:
不想 gulp 任务列表被「私有任务」弄乱(比如不把任务暴露给命令行工具)
请注意 gulp (或者 Grunt)本身并不是所有场景的最佳选择。比如,如果仅是合并、压缩几个 JavaScript,或者是编译 SASS,可能应该考虑使用 Make 或者 npm run,通过命令行完成。更少的依赖、更少的配置才是正解。
阅读 使用 npm run 实现任务自动化 以了解更多。首先我们要明确需要通过过构建实现什么,然后选择最适合的工具。
当然,gulp 确实是一个优秀的构建系统,我(作者,译者也是)很喜欢使用,也让我领略到 Node.js stream 的强大。
每周一,周四下午准点为您送上前端的最新文章点评,快点击顶部蓝色
腾讯FERD关注我们吧。
以上是关于点评君第10期Gulp 范儿——Gulp 高级技巧的主要内容,如果未能解决你的问题,请参考以下文章
点评君第24期响应式编程(Reactive Programming)介绍
开源Nodejs项目推荐gulp核心模块:Orchestrator
gulp技巧总结
第879期使用 Babel 和 Gulp 搭建 ES6 开发环境
第140期Gulp新手入门教程
前端构建工具gulp.js的使用介绍及技巧