Gulp.js深入讲解
来源:互联网 发布:国内打车软件排名 编辑:程序博客网 时间:2024/06/09 22:55
上周明河发了篇《Gulp.js—比Grunt更易用的前端构建工具》,同时也贴在阿里的技术站上,引起了不少前端同学的讨论。
赤温(开发工程师)说:这东西(Gulp.js与Grunt)差距不大,社区才是王道!
英布(高级前端开发工程师)说:一个是以配置的方式,一个是以编程的方式,感觉各有千秋吧,复杂流程还是使用编程的方式比较好,简单的那就随便了。
诚冉(前端开发工程师)说:曾经苦于将grunt的各个工具黏合在一起的过程,grunt这个东西生出来就不是给小项目用的,必须是一定规模的并且持续更新的项目,才舍得花这么大力气来“制造工具”。一点也不夸张,虽然grunt的组件越来越多,但概念就像import一样,称不上是框架,顶多是一种合作机制。Nodejs的世界里,好像不跟stream和pipe沾点边的概念都不好用似的,现在写Web服务框架,基本都跟connect一个风格,req和res一层一层的流经各个handler。刚一看到glup下意识的觉得有点俗气了,但是想想Unix的哲学,标准化的输入输出,不需要过多的元信息描述,简单实用,组件连接起来产生的巨大威力,任何人都无法拒绝!
猎隼(资深前端开发工程师)说:基于node.js的工具有一点让人深恶痛绝:会创建一个文件夹,而且里边的文件有大量的冗余。这对项目文件是一种污染,很有可能让一些不明真相的工具把这些代码也一起搜索了,比如一些查找替换程序。比较喜欢ruby的管理方式,需要的包在本机只需要安装一次,而且不会像node那样创建一个文件夹
半边同学推荐了一个讨论Gulp.js与Grunt的issue,明河也推荐一篇文章《No Need To Grunt, Take A Gulp Of Fresh Air》。
Gulp.js处理构建任务如下图:
Grunt处理任务:
(上图来自http://slid.es/contra/gulp)
理解Gulp.js的核心设计
streaming很容易联想到河流,河流有哪些特征?流动、源源不断、具有方向、有起点、有终点,而这些特征也是Gulp.js构建代码的特点。
Gulp.js官网上的简介是The streaming build system,核心的词是streaming(流动式),如何理解这个词呢?《Gulp.js—比Grunt更易用的前端构建工具》,明河讲到Gulp.js的精髓在于对Node.js中Streams API的利用,所以想要理解Gulp.js,我们必须理解Streams,streaming其实就是Streams的设计思想。
流(stream)的概念来自unix,核心思想是do one thing well,一个大工程系统应该是由各个小且独立的管子连接而成,如诚冉同学所说:Unix的哲学,标准化的输入输出,不需要过多的元信息描述,简单实用,组件连接起来产生的巨大威力,任何人都无法拒绝!,如下图:
推荐下《stream-handbook》,非常详细地说明了如何使用Streams,也提供了很多Streams的第三方模块供参考。
我们以经典的Nodejs读取文件逻辑来说明Streams与传统方式的差异。
使用fs模块读取一个json文件
传统的方式:
var dataJson, fs;fs = require('fs');dataJson = './gallery-db/component-info.json';exports.all = function(req, res) { fs.readFile(dataJson,function(err, data){ if (err) { console.log(err); } else { res.end(data); } });};
fs.readFile()是将文件全部读取放进内存,然后触发回调,这种方式存在二方面的瓶颈。
1.读取大文件时容易造成内存泄漏
比如一个上传视屏的服务,面对少者二三百M,多者七八G的文件,明显这种方式不可行,所以我们需要分断上传,如果使用传统方式,就会发现非常蛋疼,估计没人想这么干….
2.深恶痛绝的回调问题
这是我自己写NodeJs代码最深恶痛绝的地方,在提供大的服务时,比如读取4-5个文件,回调层层嵌套,可读性和维护性非常大,恶心指数陡增。
而且每次你都要对err进行处理:
if (err) { console.log(err);} else { //...}
代码啰嗦,不够简练。koa已经解决了这个问题,NodeJs的发展史就是跟回调斗争史啊。
流的方式:
var dataJson, fs;fs = require('fs');dataJson = './gallery-db/component-info.json';exports.all = function(req, res) { var stream; stream = fs.createReadStream(dataJson); stream.on('error', function(err) { return console.log(err); }); stream.on('data', function(chunk) { return console.log('got %d bytes of data', chunk.length); }); return stream.pipe(res);};
fs.createReadStream()创建一个readable streams(只读的流),请看《stream-handbook》中的readable streams部分,readable streams是有状态的,比如可以监听其data事件,读取数据后会触发,比如error事件,读取数据失败后会触发。
stream.pipe(res),pipe方法是流对象的核心方法,这句代码可以理解为,res(结果集,实际上是一个writeable streams,写流)对象接收从stream而来的数据,并予以处理输出。所以Gulp.js中的pipe()方法并不是gulp的方法,而是流对象的方法。pipe()返回的是res的返回的对象。
上述代码会把component-info.json的内容打印出来。
流的种类有:readable streams、writeable streams(比如Gulp.js中的gulp.dest(‘./build’)f方法)、transform(明河很不喜欢这个词,词不达意,称之为through更为合适,表示这是读/写流,后面会详细讲解)streams,duplex streams(duplex可以理解为循环的流,比如服务器端和客户端的实时通信,由于与Gulp.js无关,不展开讲)。
如何开发个Gulp.js插件?
所有的Gulp.js插件基本都是through(后面不再使用transform这个词) streams,即是消费者(接收gulp.src() 传递出来的数据,然后进行处理加工处理),又是生产者(将加工后的数据传递出去),我们以gulp-clean为例(这个插件用于清理目录和文件)。
先看下gulp-clean的API用法:
var gulp = require('gulp');var clean = require('gulp-clean');gulp.task('default', function() { gulp.src('app/tmp') .pipe(clean());});
非常简单的代码,目的是残忍地将tmp目录干掉。
gulp-clean的源码传送门。
引入依赖模块
var rimraf = require('rimraf');var es = require('event-stream');var gutil = require('gulp-util');var path = require('path');
rimraf 模块用于删除目录。
gulp-clean插件使用了个through streams的包装模块:event-stream,用于简化streams调用的api,明河推荐使用through2,会更好用。
path 模块就不说了,大家太熟悉了。
定义供外部调用插件方法
module.exports = function (options) { return es.map(function (file, cb) { //具体业务逻辑 });};
es.map()会创建一个through streams,我们只要关注写业务逻辑就好,不用关注如何创建streams。
file为上游读流产生的文件数据,cb是核心方法用于向下游传递处理后的数据。
return cb(null, file);
具体业务代码
// 获取文件绝对路径var filepath = file.path;var cwd = file.cwd;var relative = path.relative(cwd, filepath);// 防止路径错误if (!(relative.substr(0, 2) === '..') && relative !== '' || (options ? (options.force && typeof options.force === 'boolean') : false)) { //删除目录 rimraf(filepath, function (error) { if (!error) { return cb(null, file); } else { return cb(new Error('Unable to delete "' + filepath + '" file (' + error.message + ').'), file); } });//打印错误消息} else if (relative === '') { gutil.log('gulp-clean: Cannot delete current working directory. (' + filepath + ')'); return cb(null, file);}//打印错误消息else { gutil.log('gulp-clean: Cannot delete files outside the current working directory. (' + filepath + ')'); return cb(null, file);}
代码非常简单,获取文件(目录)路径,调用rimraf()删除之即可。
一个简单的Gulp.js插件就完成了,so easy!
总结
Gulp.js的使用和插件的开发都很简单,当然里面还有很多细节,明河就不展开讲了,请看Gulp.js的官方文档。
- Gulp.js深入讲解
- Gulp.js
- js基本数据类型-深入讲解(1)
- js基本数据类型-深入讲解(2)
- gulp配置文件gulpfile.js
- gulp压缩js代码
- gulp-uglify《JS压缩》
- gulp压缩js!!
- gulp.js 核心代码
- Gulp.js介绍
- 【gulp.js】Gulp中的增量编译
- gulp-uglify《JS压缩》----gulp系列(四)
- 深入阅读gulp源码小结
- js基本数据类型-深入讲解(3)-小案例
- gulp压缩js和css
- gulp压缩js和css
- gulp压缩js应用示例
- Gulp.js入门小教程
- 猴年回家前的思绪
- USB HID介绍
- 一个游戏程序员的App移动互联网创业
- Java学习——Servlet跳转的配置
- 机器学习中的EM算法详解及R语言实例(2)
- Gulp.js深入讲解
- CentOS7_装机软件推荐
- 命令行下JSON处理工具:jq
- js的一些总结
- 【数据库】--postgres学习
- 贝叶斯的路——概率论迷思
- FZU 1686 神龙的难题(重复覆盖|Dancing Links)
- java面向对象的基础扫描
- 【推荐】大规模的自然场景文字检测与识别数据库