task 烤箱能不能代替微波炉stream

大数据-Storm
在Storm中, 开发者可以为上游spout/bolt发射出的tuples指定下游bolt的哪个/哪些task(s)来处理该tuples。这种指定在storm中叫做对stream的分组,即stream grouping,分组方式主要有以下7种
Shuffle Grouping 或None GroupingFields GroupingAll GroupingGlobal GroupingLocalOrShuffle GroupingDirect Grouping
1.Shuffle Grouping或None Grouping
&&& Shufflegrouping: Tuples are randomly distributed across the bolt's tasks in a way suchthat each bolt is guaranteed to get an equal number of tuples.
&&& Nonegrouping: This grouping specifies that you don't care how the stream isgrouped. Currently, none groupings are equivalent to shuffle groupings.Eventually though, Storm will push down bolts with none groupings to execute inthe same thread as the bolt
or spout they subscribe from (when possible).
——官方文档
1.2 图示与解读
&&& 上游spout/bolt发射的tuples被随机地在下游bolt的tasks中选择一个来处理。如下图:
1.3 优缺点
&&& 优点:为tuple选择task的代价小;
bolt的tasks之间的负载比较均衡;
&&& 缺点:上下游components之间的逻辑组织关系不明显;
1.4 适用场景举例
Shuffle grouping的方式在实际Topology中十分常见。Storm-starter中的ExclamationTopology即为典型一例。
ExclamationTopology实现的功能是:word-spout不断地向exclaim1-bolt发射随机单词,exclaim1-bolt每收到一个单词就追加“!!!”然后再发射给exclaim2-bolt,exclaim2-bolt每收到一个单词也追加“!!!”然后发射出来,最终每个单词都被追加了“!!!!!!”。
下面是ExclamationTopology中的关键代码。主函数:
在主函数中首先通过TopologyBuilder的对象定义了id为word的spout与id为exclaim1和exclaim2的两个bolt,分别在TestWordSpout、ExclamationBolt中实现。setSpout/setBolt()的最后一个参数是该component的并行度,即task数目,例如有10个task并行运行word-spout。shuffleGrouping(&word&)定义exclaim1-bolt以shuffleGrouping的方式从word-spout接收tuple,即word-spout发射出的任何一个tuple被exclaim2-bolt中随机的一个task接收并处理。
下面是ExclamationTopology的运行结果:
在运行中,exclaim1-bolt有3个task,分别是:exclaim1:2/3/4。从上面的运行结果看,exclaim1-bolt在接收word-spout的tuple时采用随机选择task的方式:exclaim1:3/4/4/3/2/4/4/2,这是在主函数中shuffleGrouping(&word&)时定义的。
2.Fields Grouping
&&& Thestream is partitioned by the fields specified in the grouping. For example, ifthe stream is grouped by the &user-id& field, tuples with the same&user-id& will always go to the same task, but tuples with different&user-id&'s may go to different tasks.
——官方文档
2.2 图示与解读
&&& 下游bolt的每个task只接受并处理某些user-id的tuples,其他的tuples由这个bolt的其他task接受并处理。如下图:
2.3 优缺点
&&& 优点:上下游components之间的逻辑组织关系显著;
&&& 缺点:付出为tuple选择task的代价;
bolt的tasks之间的负载可能不均衡,根据field字段而定;
2.4 适用场景举例
&&& Fieldsgrouping的方式在实际Topology中十分常见,主要用来做tuple的分类合并等。Storm-starter中的WordCountTopology即为典型一例。
&&& WordCountTopology实现的主要功能是:spout-spout不断向split-bolt发射随机句子,split-bolt将收到的句子分割成单词并发射给count-bolt,count-bolt对收到的每个单词计数,并发射该单词的计数值,最终实现对句子中所有单词计数。
&&& 下面是WordCountTopology中的关键代码。主函数:
&&& 在主函数中,定义了spout-spout、split-bolt和count-bolt三个component,分别由RandomSentenceSpout、SplitSentence和WordCount实现。shuffleGrouping(&spout&)定义split-bolt以shuffleGroupig的方式从spout-spout接收tuple。fieldsGrouping(&split&,new Fields(&word&))定义count-bolt以fieldsGrouping的方式从split-bolt接收tuple,分类的标准是split-bolt发射出的tuple中的word字段值,例如所有word字段为“apple”的tuple都进入count-bolt的同一个task。split-bolt发射的tuple的字段在类SplitSentence中的declareOutputFields()中定义:
下面是WordCountTopology的运行结果:
&&& 在运行中,count-bolt有12个task。从运行结果看到,包含单词“the”的tuple全部进入count:3这个task,而包含单词“seven”的tuple全部进入count:2这个task。这就是在主函数中定义的fieldsGrouping(&split&, new Fields(&word&))的作用。
3. AllGrouping
&&& Thestream is replicated across all the bolt's tasks. Use this grouping with care.
——官方文档
3.2 图示与解读
&&& 上游spout/bolt发射出的任何一个tuple都会复制发送给下游bolt的所有task,相当于广播方式。如下图:
3.3 优缺点
&&& 优点:上游事件可以通知下游bolt中所有task;
&&& 缺点:tuple消息冗余,对性能有损耗,请谨慎使用;
3.4 适用场景举例
&&& 由于对性能的损耗,All Grouping的方式在实际Topology中并不常用。但在某些场景中,例如希望上游spout/bolt发出的任一tuple下游bolt的所有task都能收到并做不同的处理,all grouping显得不可或缺。下面以All Grouping的测试示例myAllGroupingTestTopology来说明它的作用。
&&& myAllGroupingTestTopology的主要功能是:myIntsSpout-spout不断产生自增tuple,例如第一次发射tuple[1,2,3],第二次发射tuple[4,5,6]等;randomIntGetterBolt-bolt收到tuple时从三个字段中随机选择一个发射给testBolt-bolt;testBolt-bolt将收到的tuple重新发射出来。
&&& 下面是myAllGroupingTestTopology中的关键代码。主函数:
&&& 主函数中定义了myIntsSpout-spout、randomIntGetterBolt-bolt、testBolt-bolt三个组件。allGrouping(&myIntsSpout&)定义randomIntGetterBolt-bolt以allGrouping的方式从myIntsSpout-spout接收tuple,即myIntsSpout-spout发出的每个tuple会被复制发送给randomIntGetterBolt-bolt的每个task。
&&& 下面是myAllGroupingTestTopology的运行结果:
&&& 在运行中,myIntsSpout-spout有2个task:myIntsSpout:4/5,randomIntGetterBolt-bolt有3个task:randomIntGetterBolt:6/7/8。从运行结果看,由myIntsSpout:4发出的tuple[475,476,477]会被复制成3份,分别发送给randomIntGetterBolt-bolt:6/7/8。这就是主函数中定义allGrouping(&myIntsSpout&)的作用。
4. GlobalGrouping
&&& The entire stream goes to a single one ofthe bolt's tasks. Specifically, it goes to the task with the lowest id.
——官方文档
4.2 图示与解读
&&& 上游spout/bolt发射的所有tuple全部进入下游bolt的同一个task,通常选择下游bolt中id最小的task。如下图:
4.3 优缺点
&&& 优点:所有上游消息全部汇总,便于合并、统计等;
&&& 缺点:bolt的tasks之间的负载可能不均衡,id最小的task负载过重;
4.4 适用场景举例
&&& GlobalGrouping方式在实际的Topology中比较常见。我们将WordCountTopology稍微改动一下,变成统计所有单词出现次数的前3,即TopNTopology,即可看到GlobalGrouping的作用。
&&& TopNTopology完成的主要功能是:spout-spout不断向split-bolt发射随机句子,split-bolt将收到的句子分割成单词并发射给count-bolt,count-bolt对收到的每个单词计数,并向TopN-bolt发射该单词及其计数值,TopN-bolt收将收到的所有tuple统计汇总,计算并发射所有单词的TopN。
&&& 下面是TopNTopology的关键代码。主函数:
&&& 在主函数中定义了多个spout/bolt,参见2.4,不再赘述。主函数中定义了TopN-bolt,由TopN实现(如下图),并行度为3。globalGrouping(&count&)定义TopN-bolt以globalGrouping的方式从count-bolt接收tuple,即所有count-bolt发射的tuple均由TopN中id最小的task接收并处理。TopN对收到的每个tuple做如下处理:将tuple中的&word,count&放入HashMap中,并排序后取前3,随后发射,具体实现如下图:
&&& 下面是TopNTopology的运行结果:
&&& 在运行中,TopN-bolt有3个task。从运行结果来看,TopN-bolt收到的所有tuple均由TopN:1来处理,TopN-bolt的其他task并未参与处理。这就是主函数中定义globalGrouping(&count&)的作用。
5.LocalOrShuffle Grouping
&&& If the target bolt has one or more tasks inthe same worker process, tuples will be shuffled to just those in-processtasks. Otherwise, this acts like a normal shuffle grouping.
——官方文档
5.2 图示与解读
&&& 如果下游bolt的某些task与上游spout/bolt的某些task运行在同一个worker进程中,那么上游spout/bolt的这些task所发射的所有tuples均由下游bolt的同进程的tasks来处理;否则,这种分组方式等同于shuffle grouping。如下图:
5.3 优缺点
&&& 优点:相对于ShuffleGrouping,因优先选择同进程task间传输而降低tuple网络传输代价,但因寻找同进程的task而消耗CPU和内存资源,因此应视情况来确定选择ShuffleGrouping或LocalOrShuffleGrouping;
&&& 缺点:上下游components之间的逻辑组织关系不明显;
5.4 适用场景举例
&&& LocalOrShuffleGrouping在实际应用中较不常见,多数被ShuffleGrouping代替。Storm-starter中的例程没有使用LocalOrShuffleGrouping的Topology。下面通过改写ExclamationTopology成为myExclamationTopology来看LocalOrShuffleGrouping的作用。
&&& myExclamationTopology的主要功能是:word-spout不断地向exclaim1-bolt发射随机单词,exclaim1-bolt每收到一个单词就追加“!!!”然后再发射给exclaim2-bolt,exclaim2-bolt每收到一个单词也追加“!!!”然后发射给testBolt-bolt,testBolt-bolt将收到的tuple不做处理再次发射出来,最终每个单词都被追加了“!!!!!!”。
&&& 下面是myExclamationTopology中的关键代码。主函数:
&&& 主函数中word-spout、exclaim1-bolt和exclaim2-bolt的定义参见1.4,不再赘述。localOrShuffleGrouping(&exclaim1&)定义exclaim2-bolt以localOrShuffleGrouping的方式从exclaim1-bolt接收tuple。
&&& 下面是myExclamationTopology的运行结果:
&&& 在运行中,exclaim1-bolt只有1个task:exclaim1:4,在进程6703中;exclaim2-bolt有3个task:exclaim2:5/6/7,分别在进程03中。所以,exclaim1:4和exclaim2:7都处于6703中。从运行结果看,exclaim1发射的所有tuple全部由exclaim2:7处理。这是主函数中定义localOrShuffleGrouping(&exclaim1&)的作用。
6.Direct Grouping
&&& This is a special kind of grouping. A streamgrouped this way means thatthe producer of the tuple decideswhich task of the consumer will receive this tuple. Direct groupings can onlybe declared on streams that have
been declared asdirect streams. Tuplesemitted to a direct stream must be emitted using one of theemitDirectmethods. A bolt can get thetask ids of its consumers by eitherusing the
provided TopologyContext or by keeping track of the output of theemit method in OutputCollector (which returns the task ids that the tuple wassent to).
——官方文档
6.2 图示与解读
&&& Direct Grouping允许上游spout/bolt决定其发射出的任一tuple由下游bolt的哪个task接收并处理。如下图:
使用Direct Grouping需进行如下四步:
1)在上游spout/bolt的declareOutputFields()函数的实现中通过调用OutputFieldsDeclarer.declareStream(StringstreamId, boolean direct, Fields fields)函数来声明该上游spout/bolt的一个direct stream,其中streamId表示该stream的名字,fields表示在该stream上发送的tuple的字段,direct目前尚未发现有什么作用,将其设置为true/false都可以实现direct
Grouping,但推荐设置为true,因为在未来的storm版本中direct参数可能有作用。
此外,经过试验,如果此处不通过OutputFieldsDeclarer.declareStream (String streamId, boolean direct,Fields fields)函数来声明一个direct stream,而是通过其他函数OutputFieldsDeclarer.declareStream(String streamId, Fields fields)或declare(Fields fields)来声明一个普通stream或default stream,这样的stream也可以被用来实现direct
grouping。但依然推荐使用OutputFieldsDeclarer.declareStream(StringstreamId, boolean direct, Fields fields)函数来声明一个directstream从而实现directGrouping,原因是在未来的storm版本中directGrouping可能只能通过direct stream支持direct grouping。
2)在Topology定义中通过调用setBolt(下游bolt).directGrouping (String componentId,String streamId)定义上游spout/bolt到下游bolt的directGrouping,其中setBolt()和directGrouping()都返回InputDeclarer对象,componentId表示上游spout/bolt的Id,streamId表示上游spout/bolt的一个direct
stream的Id。
3)此时可以在上游spout/bolt中(如execute()函数的实现)调用emitDirect(int taskId, String streamId, Tuple anchor, List&Object&tuple)函数,但在此之前需要获得下游bolt的所有taskId以便调用。在spout/bolt的open()或prepare()函数实现中通过调用TopologyContext.getCompoentTasks(StringcomponentId)可以得到下游bolt的所有taskId。
4)在上游spout/bolt中(如execute()函数的实现)通过调用OutputCollector.emitDirect(inttaskId, String streamId, Tuple anchor, List&Object& tuple)函数利用该spout/bolt的direct stream向下游bolt的特定task发射tuple。其中taskId表示下游bolt指定的task的Id;streamId表示上游spout/bolt的一个direct
stream的Id;tuple表示需要发射的tuple;anchor表示输入tuple,storm以此来建立tuple树,便于追踪(OutputCollector. ack(Tuple input)是用来做ack的。参考:Storm原理与实现-星星的日志-网易博客.pdf中1.2.1 普通Topology)。
6.3 优缺点
&&& 优点:Topology的可控性强,且组件的各task的负载可控;
&&& 缺点:当实际负载与预估不符时性能削弱;
6.4 试用场景举例
&&& DirectGrouping在实际Topology中较不常见,在Storm-starter的例程中没有使用DirectGrouping的Topology。下面通过改写ExclamationTopology成为myStreamTestTopology来看DirectGrouping的作用。另外,在Oreilly.Getting.Started.with.Storm.Aug.2012.pdf中p24也有一个例子,通过directGrouping方式实现FieldGrouping。
&&& myStreamTestTopology的主要功能是:word-spout不断地向exclaim1-bolt发射随机单词,exclaim1-bolt每收到一个单词分别追加“!”和“!!”然后以directGrouping的方式再发射给testBolt-bolt的第1个(起始为0)task,testBolt-bolt每收到一个tuple取其第一个字段发射给testBolt2-bolt,testBolt2-bolt将收到的tuple不做处理再次发射出来。
&&& 下面是myStreamTestTopology的关键代码。主函数:
&&& 主函数中word-spout,exclaim1-bolt,testBolt-bolt,testBolt2-bolt的定义参见1.4,不再赘述。directGrouping(&exclaim1&,&outPutStream1&)定义exclaim1-bolt以directGrouping的方式向testBolt-bolt发射tuple。
在exclaim1-bolt中首先定义名为outPutStream1/2的两个direct stream,然后得到testBolt-bolt的所有taskId,最后调用emitDirect()通过outPutStream1将新tuple发射到testBolt-bolt的第1个(起始为0)task。其具体实现如下图:
&&& 下面是myStreamTestTopology的运行结果:
&&& 在运行中,testBolt-bolt共有三个task:testBolt:6/7/8。由于exclaim1-bolt中指定tuple由testBolt-bolt的第1个task,即testBolt:7来接收并处理。从运行结果来看,exclaim1-bolt发射的tuple均由testBolt:7处理。这是directGrouping的作用。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:68次
排名:千里之外Taskstream: Assessment, Accreditation, e-Portfolios to Improve Student Learning
Assessment & Accreditation
Assessment for improvement requires planning, coordination, measurement, reflection, and action. Manage the entire cycle more effectively and engage your community more deeply in the process.
e-Portfolios
Encourage integrative, intentional learning through reflection and demonstration of achievements. Collect direct evidence and evaluation data to inform improvements at course, program, and institutional levels.
Educator Preparation
Streamline assessment data collection, field and clinical placement management, state and SPA reporting, accreditation preparation, and provide better support to candidates preparing for the edTPA(TM).
Taskstream is proud to partner with the Association of American Colleges and Universities (AAC&U), State Higher Education Executive Officers (SHEEO), and the Multi-State Collaborative to Advance Learning Outcomes Assessment (MSC) as they seek to create a nationally scalable approach to outcomes assessment based on authentic student work.
The team at UMass Amherst worked with Taskstream on a portfolio pilot to complement their “SBS Pathways” philosophy, which integrates advising and curriculum and helps students be more intentional in …
This webinar presents an overview of best practices in team-based collaborative assessment by using the Association of College and Research Libraries’ Assessment in Action (AiA) project (involving over 200 campus …
By Emily Mayer
& As illustrated in a previous blog post, a curriculum map illustrates where within a curriculum student learning outcomes are taught and can be assessed. This process ensures that alignment exists …
By Amy Lebo
& When students hope that college will be “the best years of their lives,” we know that they are not just excited about their coursework. As the director of an …
By Jeff Reid
& Next week we are headed to the Assessment Institute in Indianapolis. We hope to see many of you there, and if you do catch us at our booth, we …常用gulp插件介绍(一) - 掘金
阅读 3108收藏 37原文链接:文章介绍了 Gulp 下多个插件工具,包括我自己也常用的 gulp-util, gulp-filter 和 gulp-inject 等 —— 由
分享在写的过程中,gulp这一块发现了很多非常实用的插件,大大的增加了能自动化的范围,这篇文章就分门别类的简单介绍下常用的gulp插件吧。
util工具类
这个分类下主要介绍一些辅助工具类的插件。
顾名思义,本插件的功能就是帮你自动require你在package.json中声明的依赖。只要一句var $ = require('gulp-load-plugins')(),则package.json中声明的gulp-或gulp.开头的插件就会自动被放在变量$下面。如$.util就等于require('gulp-util'),而有两个连字符的插件则会自动命名为驼峰格式,如$.taskListing则等于require('gulp-task-listing')。有了这个插件,就不用一个一个的require了。这个插件还有一些常用的参数配置,这里列几个常用的:
lazyload: true,用到这个插件的时候再去require,默认为true。
rename: {'gulp-task-listing': 'list'},如果有些插件名字太长,可以使用该参数重命名。
scope: ['dependencies'],本插件默认会扫描package.json里的所有dependence,可以使用该参数进行限制。
要使用这些参数只要在require的时候传入即可,如require('gulp-load-plugins')({lazy: true})。
这个插件的作用也很容易猜,它可以打印出gulpfile.js中定义的所有task,这个插件我们在这篇文章的最后介绍过,值得一提的是它还可以根据task的名字确定它是不是一个子task,比如带有:、-、_的task就被认为是子task。我一般把这个插件作为默认的task来调用,如
gulp.task('default', ['help']);
gulp.task('help', $.taskListing);
这样,如果只执行gulp的话就会打印出所有定义好的task,非常实用。
严格的说,yargs不是专门用于gulp的,它是Node中处理命令行参数的通用解决方案。只要一句代码var args = require('yargs').就可以让命令行的参数都放在变量args上,非常方便。它可以处理的参数类型也是多种多样的:
单字符的简单参数,如传入-m=5或-m 5,则可得到args.m = 5。
多字符参数(必须使用双连字符),如传入--test=5或--test 5,则可得到args.test = 5。
不带值的参数,如传入--mock,则会被认为是布尔类型的参数,可得到args.mock = true。
除此之外,还支持很多其他类型的传参方式,具体可参考。
gulp-util带有很多方便的函数,其中最常用的应该就是log了。$.util.log()支持传入多个参数,打印结果会将多个参数用空格连接起来。它与console.log的区别就是所有$.util.log的结果会自动带上时间前缀。另外,它还支持颜色,如$.util.log($.util.colors.magenta('123'));打印出来的123是品红色的。其实$.util.colors就是一个
的实例,而chalk是专门用来处理命令行打印着色的一个工具。
grunt自身提供一个用来处理支持glob匹配的删除,而del就是gulp上对应的工具。del支持和gulp.src参数同样的匹配,除此之外,它的第二个参数还支持一个回调函数,当删除完成以后执行,所以这是一个异步的删除。常用的调用方法为:del([xxx], callback)。
这是一个统计文件大小变化的工具,通常与压缩类工具放在一起实用,比如
gulp.src('**/*.html')
.pipe($.bytediff.start())
.pipe($.minifyHtml({empty: true}))
.pipe($.bytediff.stop(bytediffFormatter))
.pipe(gulp.dest('dist'));
function bytediffFormatter (data) {
var difference = (data.savings & 0) ? ' smaller.' : ' larger.';
return data.fileName + ' went from ' +
(data.startSize / 1000).toFixed(2) + ' kB to ' +
(data.endSize / 1000).toFixed(2) + ' kB and is ' +
formatPercent(1 - data.percent, 2) + '%' +
在压缩的pipe前后加上$.bytediff.start()和$.bytediff.stop(callback),即可统计压缩前后文件的变化。在callback中传入的参数data上,可以访问到很多变量,如文件名,变化前后的大小,变化百分比等等。
这个插件的作用很简单,打印出stream里面的所有文件名,通常调试的时候比较需要。
这个插件也可以顾名思义:用来升级版本用的,废话不说,直接看例子吧:
return gulp
.src('package.json')
.pipe($.bump(options))
.pipe(gulp.dest('dist'));
重点来看这里的options,我们可直接传递一个具体的version进去,也可以按照Node的版本规范传递一个type进去,让其自己生成对应的version:
version,直接传递要升级到的版本号,如1.2.3。
type,可接受的值包括下面四个,倘若现在的版本号为1.2.3,则对应的新版本号为:
prerelease:1.2.3-0
patch:1.2.4
minor:1.3.0
major:2.0.0
最终这个升级后的版本号会反映在package.json中,当然,你也可以在gulp.src中传入更多的文件(如bower.json)来替换更多的文件。
这个工具用来在压缩后的JS、CSS文件中添加头部注释,你可以包含任意想要的信息,通常就是作者、描述、版本号、license等,比如:
function getHeader () {
var pkg = require('package.json');
var template = ['/**',
' * &%= pkg.name %& - &%= pkg.description %&',
' * @authors &%= pkg.authors %&',
' * @version v&%= pkg.version %&',
' * @link &%= pkg.homepage %&',
' * @license &%= pkg.license %&',
' */',
''
].join('\n');
return $.header(template, {
这个函数将package.json中的各种信息提取出来,变成头部注释,只要在压缩的pipe中调用.pipe(getHeader())即可。
stream相关
这个部分主要介绍一些跟stream操作有关的插件。
gulp-filter可以把stream里的文件根据一定的规则进行筛选过滤。比如gulp.src中传入匹配符匹配了很多文件,可以把这些文件pipe给gulp-filter作二次筛选,如gulp.src('**/*.js').pipe($.filter(**/a/*.js)),本来选中了所有子文件下的js文件,经过筛选后变成名为a的子文件夹下的js文件。那有人要问了,为什么不直接将需要的筛选传入gulp.src,干嘛要多筛选一步呢?这里面有两种情况:
gulp.src与$.filter中间可能需要别的处理,比如我对所有文件做了操作1以后,还需要筛选出一部分做操作2。
第二种情况就要谈到gulp-filter的另外一个特性:筛选之后还可以restore回去。比如我对所有文件做了操作1,筛选了一部分做操作2,最后要把所有的文件都拷贝到最终的位置。代码如下:
var filter = $.filter('**/a/*.js');
gulp.src('**/*.js')
.pipe(action1())
.pipe(filter)
.pipe(action2())
.pipe(filter.restore())
.pipe(gulp.dest('dist'))
可以看到,如果没有restore这个操作,那么拷贝到最终位置的文件将只包含被过滤出来的文件,这样一restore,所有的文件都被拷贝了。
gulp-flatten非常实用,可能知道别的库中flatten函数的同学已经猜到它是干嘛的了。比如gulp.src('**/*.js')匹配了很多文件,包括a/b/c.js,d/e.js,f/g/h/i/j/k.js,l.js,这些文件的层级都不一样,一旦我们将这个文件pipe给$.flatten(),则所有的文件夹层级都会去掉,最终的文件将是c.js,e.js,k.js,l.js,在一些场景下还是非常有用的。
这个插件的作用简单来说就是一旦pipe中的某一steam报错了,保证下面的steam还继续执行。因为pipe默认的onerror函数会把剩下的stream给unpipe掉,这个插件阻止了这种行为。那它一般用于哪种场景呢?比如,代码每次build之前要跑一遍jshint和jscs来确保所有代码都符合规范,但一旦某些代码不符合规范,整个build流程就会停止,这个时候就需要gulp-plumber出场了。如:
gulp.task('build', ['jslint', 'xxxx']);
gulp.task('jslint', function () {
return gulp
.src(config.js.all)
.pipe($.plumber())
.pipe($.jshint())
.pipe($.jscs());
这样,一旦jshint或jscs报错,整个build流程还是可以继续走下去的,而且gulp-plumber会给出一个报错提醒我们:
[16:52:36] Plumber found unhandled error:
Error in plugin 'gulp-jshint'
JSHint failed for: xxxx.js
这个插件的功能也很简单,可以条件性的添加stream,如.pipe($.if(flag, action1())),则只会在flag变量为true时才会将action1()添加到stream中去。其实不用这个插件也可以达到类似的效果,那就是gulp-util里有一个函数叫做noop(),也就是no
operation,这个函数其实是返回一个什么都不干的空stream。利用这个函数我们可以这么写:.pipe(flag ? action1() : $.util.noop()),与上例的效果是一样的。
一个gulp的task只能返回一个stream,但有的时候有这么一种情景:有两类文件,它们的原始位置和处理后的位置都是不同的,但它们的处理流程相同。由于gulp.src和gulp.dest的参数不同,我们就需要写两个task来分别完成这个任务,一方面略显重复,另一方面逻辑上来讲这两个task本来就是处理同样的事情的。这种情况就需要merge-stream登场了,它的作用就是将多个stream合成一个返回。比如下面这个例子:
var merge = require('merge-stream');
gulp.task('jade', function () {
var stream1 = jade(src1, dest1);
var stream2 = jade(src2, dest2);
return merge(stream1, stream2);
function jade (src, dest) {
return gulp
.pipe($.jade())
.pipe(gulp.dest(dest));
可以看到,处理的流程被提取出来放入一个函数,它接受两个参数,分别是src和dest。然后在task中直接调用这个函数生成两个stream,然后返回merge-stream合并后的结果。
gulp里的task都是异步并发执行的,有的时候我们需要一连串的task按顺序执行,这时就需要run-sequence登场了。它的调用很简单:runSequence('task1', 'task2', ['task3', 'task4'], 'task5'),这里的task都是gulp定义好的task名称,task1完成后才会执行task2,以此类推。注意到task3和task4被放在中括号里了,这表明,task3和task4可以并发执行的,但两个都执行完后才会执行task5。这里要说明的是,每个task要么返回一个stream,即return gulp.src().pipe().pipe(),要么支持回调函数,即gulp.task('task1', function (done) { action1(done); }),满足了这两点才能保证正常的执行顺序,因为这是gulp对
inject相关
这个部分主要介绍一些将JS/CSS自动插入到HTML的相关插件。
wiredep就是wire dependence的意思,它的作用就是把bower.json中声明的dependence自动的包含到HTML中去。要插入文件,wiredep需要解决两个问题:
插入的位置:wiredep通过识别HTML中的注释来识别插入位置,如
&!-- bower:css --&
&!-- endbower --&
&!-- bower:js --&
&!-- endbower --&
不同类型的文件被插入到不同的区块。
插入什么文件:要插入的文件列表自然来自bower.json,每个bower安装的依赖库,根目录下边都有一个自己的bower.json文件,其中的main字段指明了使用这个库需要包含的文件,wiredep最终包含的文件列表就来自这个字段。有些情况下,库自身的bower.json的main字段可能会多包含文件或少包含文件,如果想要定制这个列表,则可以在自己的bower.json中使用overrides字段,如下面的代码覆盖了mdi这个库的main字段。
&overrides&: {
&css/materialdesignicons.css&
wiredep插件支持很多参数,常用的主要有两个:
bowerJson:指定bower.json的内容,注意这个字段不是bower.json文件的位置,这个参数需要使用require后的结果赋值:require('bower.json')。
directory:指定存放bower安装后的依赖包的路径,通常是bower_components。注意最终插入到HTML中的文件列表的路径是index.html文件相对于本文件夹的相对路径。
使用wiredep也比较简单,直接把它传入到stream中即可,如gulp.src('index.html').pipe(wiredep(options))。
这个插件的作用与wiredep类似,不同的是可以自己任意指定需要插入文件的列表。它同样是利用注释来寻找插入的位置,它识别的默认注释为&!-- inject:js --&,但更加智能:
支持各种模板语言:可以根据gulp.src指定的源文件自动识别注释和插入内容,除了支持HTML外,还支持jade、haml等。若源为jade文件,则识别的注释为//- inject:js,插入的内容为:script(src=&&filename&.js&)。
配置非常灵活:
name:默认识别的注释标签格式为&!-- name:ext --&,这里的name默认值就是“inject”,而ext的默认值是要插入的文件的扩展名。那么name属性可配置意味着可以添加自定义的插入区块,如&!-- production:js --&,这个标签可以只插入生产环境需要包含的JS文件。
starttag和endtag:支持自定义需要识别的注释内容。
addPrefix和addSuffix:支持在插入文件的路径上自定义前缀、后缀。
relative:指定插入文件的路径是否为相对路径。
ingorePath:指定插入文件的路径前面会忽略掉指定的路径。
read:这个参数通常给false,不需要真正的去读取文件。
这个插件的使用场景通常是,我们需要index里有多个区块,比如上面name的例子,只有当为production环境编译的时候才去包含相关的文件。
这三个工具之所以放在一起讲,是因为它们一般都是一起使用的。它们要解决什么问题呢?通过上面的wiredep也好,gulp-inject也好,插入了一堆JS、CSS文件到HTML中,一旦部署到生产环境,这么多文件必然是要合并压缩的。光是压缩还不够,为了解决缓存问题,每次合并压缩后要给最终的文件加hash,这样每次文件内容一变动,hash也会跟着变动,就不存在浏览器依然使用缓存的老文件的问题。这样得到最终的文件以后,肯定还要将这个文件替换回HTML中去,一大堆的script和link标签替换成最终合并压缩带hash的版本。
前面啰啰嗦嗦的一大堆工作就是这三个插件要解决的问题了。首先,gulp-useref根据注释将HTML中需要合并压缩的区块找出来,对区块内的所有文件进行合并。注意:它只负责合并,不负责压缩!所以合并出来的文件我们要自行压缩,压缩以后调用gulp-rev负责在文件名后追加hash。最后调用gulp-rev-replace负责把最终的文件名替换回HTML中去。扯了大半天,还是直接上例子吧。先来看看HTML中的注释:
&!-- build:css static/styles/lib.css --&
&!-- bower:css --&
&!-- endbower --&
&!-- endbuild --&
&!-- build:css static/styles/app.css --&
&!-- inject:css --&
&!-- endinject --&
&!-- endbuild --&
&!-- build:js static/js/lib.js --&
&!-- bower:js --&
&!-- endbower --&
&!-- endbuild --&
&!-- build:js static/js/app.js --&
&!-- inject:js --&
&!-- endinject --&
&!-- endbuild --&
gulp-useref识别的就是build开头的注释,build后面首先跟的是类型扩展名,然后后面的路径就是build区块中的所有文件进行合并后的文件路径,这个相对路径是相对于这个HTML的路径。上面的例子中我们用build区块把bower和inject进来的文件包起来,这些文件就可以被gulp-useref合并了。再来看gulp中useref相关task的定义:
var assets = $.useref.assets({searchPath: 'app/src/'});
var cssFilter = $.filter('**/*.css');
var jsAppFilter = $.filter('**/app.js');
var jslibFilter = $.filter('**/lib.js');
return gulp
.src('index.html')
.pipe(assets)
.pipe(cssFilter)
.pipe($.csso())
.pipe(cssFilter.restore())
.pipe(jsAppFilter)
.pipe($.uglify())
.pipe(getHeader())
.pipe(jsAppFilter.restore())
.pipe(jslibFilter)
.pipe($.uglify())
.pipe(jslibFilter.restore())
.pipe($.rev())
.pipe(assets.restore())
.pipe($.useref())
.pipe($.revReplace())
.pipe(gulp.dest('dist'));
首先一上来,先调用$.useref.assets()函数,这个函数返回一个stream,包含已经合并后的文件。可以尝试在第9行后面加上前面介绍过的gulp-print插件.pipe($.print()),打印出stream里的文件,发现就是前面HTML中4个build注释块后面的4个文件。注意这里调用的时候跟了一个searchPath的参数,它的用处就是指定从哪个路径开始寻找build区块底下的文件。比如build区块底下有这么一行&script src=&static/js/a.js&&&/script&,那最终gulp-useref将从这个路径app/src/static/js/a.js找到这个文件。第3到5行定义了3个filter,这主要是为了后面压缩准备的。下面正式看stream的pipe流程。先选出要处理的HTML文件,然后调用刚才得到的assets得到合并后的4个文件,第10到12行筛选出合并后的CSS文件进行压缩(压缩类插件下篇文章再讲),第13到16行筛选出app.js进行压缩,第17到19行筛选出lib.js进行压缩。之所以要区别对待app.js和lib.js,是因为app.js是我们自己写的代码,压缩后要加上header(第15行,使用前面介绍过的gulp-header插件),而lib.js是第三方的各种库,直接压缩即可。后面调用gulp-rev给压缩后的4个文件加hash,然后调用assets.restore()将src源换回HTML文件,这是为了后面调用$.useref(),因为$.useref()做替换的src源是HTML文件,同样后面调用gulp-rev-replace将带hash的文件替换回HTML,它要求的src源也必须是HTML文件。这里的顺序很重要,因为这几个插件接受的源不一样,gulp-rev接受的是JS、CSS文件,而gulp-useref和gulp-rev-replace接受的是HTML。还有一个问题:gulp-rev-replace是怎么知道gulp-rev进行hash前后的文件名对应关系呢?其实gulp-rev会生成一个manifest的文件,内容是类似下面的JSON:
&static/styles/lib.css&: &static/styles/lib-d41d8cd98f.css&
&static/js/lib.js&: &static/js/lib-273c2cin3f.js&
当然这个文件默认是不会生成在文件系统里的,可以通过.pipe($.rev.manifest())将这个文件保存到本地。有了这个文件,gulp-rev-replace甚至可以脱离gulp-rev独立工作哦!
好了,这篇就到这里,还有好多工具没介绍到,留着给下篇吧。相关热门文章}

我要回帖

更多关于 bm7和弦能不能代替 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信