第一个gulp程序

第一个gulp程序

说起来惭愧,一直用公司内部的工具,没有用这些红得发紫的东西。今天东抄西拼终于搞出第一个gulp应用。gulp是做什么的,好处在哪儿我不废话了。直入主题吧。

先在D盘下建立一个xxxx目录,然后打开控制台,直接将npm install gulp。 里面多出一个node_modules目录安装成功。

然后xxxx目录下面建一个src目录,里面建一个index.html文件,内容如下或你自己乱写一点东西,我们这个例子主要测试压缩html。

<!DOCTYPE html>
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
    </head>
    <body>
        <h2>写在前面</h2>
        <p>本来是想写个如何编写gulp插件的科普文的,突然探究欲又发作了,于是就有了这篇东西。。。翻了下源码看了下<code>gulp.src()</code>的实现,不禁由衷感慨:肿么这么复杂。。。</p>
        <h2>进入正题</h2>
        <p>首先我们看下<code>gulpfile</code>里面的内容是长什么样子的,很有express中间件的味道是不是~<br />我们知道<code>.pipe()</code>是典型的流式操作的API。很自然的,我们会想到<code>gulp.src()</code>这个API返回的应该是个Stream对象(也许经过层层封装)。本着一探究竟的目的,花了点时间把gulp的源码大致扫了下,终于找到了答案。</p>
        <p>gulpfile.js</p>
        <pre class="hljs-dark"><code class="hljs javascript"><span class="hljs-keyword">var gulp = <span class="hljs-built_in">require(<span class="hljs-string">‘gulp‘),
    preprocess = <span class="hljs-built_in">require(<span class="hljs-string">‘gulp-preprocess‘);
 
gulp.task(<span class="hljs-string">‘default‘, <span class="hljs-function"><span class="hljs-keyword">function<span class="hljs-params">() {
 
    gulp.src(<span class="hljs-string">‘src/index.html‘)
        .pipe(preprocess({USERNAME:<span class="hljs-string">‘程序猿小卡‘}))
        .pipe(gulp.dest(<span class="hljs-string">‘dest/‘));
});
</span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
        <h2>提前剧透</h2>
        <p>此处有内容剧透,如有对剧透不适者,请自行跳过本段落。。。</p>
        <blockquote>
            <p>gulp.src() 的确返回了定制化的Stream对象。可以在github上搜索<code>ordered-read-streams</code>这个项目。</p>
            <p>大致关系是:<br />ordered-read-streams --> glob-stream --> vinyl-fs --> gulp.src()</p>
 
 
        </blockquote>
        <h2>探究之路</h2>
        <p>首先,我们看下<code>require(‘gulp‘)</code>返回了什么。从gulp的源码来看,返回了<code>Gulp</code>对象,该对象上有<code>src</code>、<code>pipe</code>、<code>dest</code>等方法。很好,找到了我们想要的<code>src</code>方法。接着往下看<br />参考:<a href="https://github.com/gulpjs/gulp/blob/master/index.js#L62" target="_blank">https://github.com/gulpjs/gulp/blob/master/index.js#L62</a></p>
        <p>gulp/index.js</p>
        <pre class="hljs-dark"><code class="hljs js"><span class="hljs-keyword">var inst = <span class="hljs-keyword">new Gulp();
<span class="hljs-built_in">module.exports = inst;
</span></span></span></code></pre>
        <p>从下面的代码可以看到,<code>gulp.src</code>方法,实际上是<code>vfs.src</code>。继续<br />参考:<a href="https://github.com/gulpjs/gulp/blob/master/index.js#L25" target="_blank">https://github.com/gulpjs/gulp/blob/master/index.js#L25</a></p>
        <p>gulp/index.js</p>
        <pre class="hljs-dark"><code class="hljs js"><span class="hljs-keyword">var vfs = <span class="hljs-built_in">require(<span class="hljs-string">‘vinyl-fs‘);
<span class="hljs-comment">// 省略很多行代码
Gulp.prototype.src = vfs.src;
</span></span></span></span></code></pre>
        <p>接下来我们看下<code>vfs.src</code>这个方法。从<code>vinyl-fs/index.js</code>可以看到,<code>vfs.src</code>实际是<code>vinyl-fs/lib/src/index.js</code>。<br />参考:<a href="https://github.com/wearefractal/vinyl-fs/blob/master/index.js" target="_blank">https://github.com/wearefractal/vinyl-fs/blob/master/index.js</a></p>
        <p>vinyl-fs/index.js</p>
        <pre class="hljs-dark"><code class="hljs js"><span class="hljs-pi">‘use strict‘;
 
<span class="hljs-built_in">module.exports = {
  src: <span class="hljs-built_in">require(<span class="hljs-string">‘./lib/src‘),
  dest: <span class="hljs-built_in">require(<span class="hljs-string">‘./lib/dest‘),
  watch: <span class="hljs-built_in">require(<span class="hljs-string">‘glob-watcher‘)
};
</span></span></span></span></span></span></span></span></code></pre>
        <p>那么,我们看下<code>vinyl-fs/lib/src/index.js</code>。可以看到,<code>gulp.src()</code>返回的,实际是<code>outputStream</code>这货,而<code>outputStream</code>是<code>gs.create(glob, options).pipe()</code>获得的,差不多接近真相了,还有几步而已。<br />参考:<a href="https://github.com/wearefractal/vinyl-fs/blob/master/lib/src/index.js#L37" target="_blank">https://github.com/wearefractal/vinyl-fs/blob/master/lib/src/index.js#L37</a></p>
        <p>vinyl-fs/lib/src/index.js</p>
        <pre class="hljs-dark"><code class="hljs js"><span class="hljs-keyword">var defaults = <span class="hljs-built_in">require(<span class="hljs-string">‘lodash.defaults‘);
<span class="hljs-keyword">var through = <span class="hljs-built_in">require(<span class="hljs-string">‘through2‘);
<span class="hljs-keyword">var gs = <span class="hljs-built_in">require(<span class="hljs-string">‘glob-stream‘);
<span class="hljs-keyword">var File = <span class="hljs-built_in">require(<span class="hljs-string">‘vinyl‘);
 
<span class="hljs-comment">// 省略非重要代码若干行
 
<span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">src<span class="hljs-params">(glob, opt) {
  <span class="hljs-comment">// 继续省略代码
 
  <span class="hljs-keyword">var globStream = gs.create(glob, options);
 
  <span class="hljs-comment">// when people write to use just pass it through
  <span class="hljs-keyword">var outputStream = globStream
    .pipe(through.obj(createFile))
    .pipe(getStats(options));
 
  <span class="hljs-keyword">if (options.read !== <span class="hljs-literal">false) {
    outputStream = outputStream
      .pipe(getContents(options));
  }
  <span class="hljs-comment">// 就是这里了
  <span class="hljs-keyword">return outputStream
    .pipe(through.obj());
}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
        <p>我们再看看<code>glob-stream/index.js</code>里的<code>create</code>方法,最后的<code>return aggregate.pipe(uniqueStream);</code>。好的,下一步就是真相了,我们去<code>ordered-read-streams</code>这个项目一探究竟。<br />参考:<a href="https://github.com/wearefractal/glob-stream/blob/master/index.js#L89" target="_blank">https://github.com/wearefractal/glob-stream/blob/master/index.js#L89</a></p>
        <p>glob-stream/index.js</p>
        <pre class="hljs-dark"><code class="hljs js"><span class="hljs-keyword">var through2 = <span class="hljs-built_in">require(<span class="hljs-string">‘through2‘);
<span class="hljs-keyword">var Combine = <span class="hljs-built_in">require(<span class="hljs-string">‘ordered-read-streams‘);
<span class="hljs-keyword">var unique = <span class="hljs-built_in">require(<span class="hljs-string">‘unique-stream‘);
 
<span class="hljs-keyword">var glob = <span class="hljs-built_in">require(<span class="hljs-string">‘glob‘);
<span class="hljs-keyword">var minimatch = <span class="hljs-built_in">require(<span class="hljs-string">‘minimatch‘);
<span class="hljs-keyword">var glob2base = <span class="hljs-built_in">require(<span class="hljs-string">‘glob2base‘);
<span class="hljs-keyword">var path = <span class="hljs-built_in">require(<span class="hljs-string">‘path‘);
 
<span class="hljs-comment">// 必须省略很多代码
 
<span class="hljs-comment">// create 方法
create: <span class="hljs-function"><span class="hljs-keyword">function<span class="hljs-params">(globs, opt) {
    <span class="hljs-comment">// 继续省略代码
<span class="hljs-comment">// create all individual streams
    <span class="hljs-keyword">var streams = positives.map(<span class="hljs-function"><span class="hljs-keyword">function<span class="hljs-params">(glob){
      <span class="hljs-keyword">return gs.createStream(glob, negatives, opt);
    });
 
    <span class="hljs-comment">// then just pipe them to a single unique stream and return it
    <span class="hljs-keyword">var aggregate = <span class="hljs-keyword">new Combine(streams);
    <span class="hljs-keyword">var uniqueStream = unique(<span class="hljs-string">‘path‘);
 
    <span class="hljs-comment">// TODO: set up streaming queue so items come in order
 
    <span class="hljs-keyword">return aggregate.pipe(uniqueStream);
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
        <p>真相来了,我们看下<code>ordered-read-streams</code>的代码,可能刚开始看不是很懂,没关系,知道它实现了自己的<code>Stream</code>就可以了(nodejs是有暴露相应的API让开发者对Stream进行定制的),具体可参考:<a href="http://www.nodejs.org/api/stream.html#stream_api_for_stream_implementors" target="_blank">http://www.nodejs.org/api/stream.html#stream_api_for_stream_implementors</a></p>
        <p>代码来自:<a href="https://github.com/armed/ordered-read-streams/blob/master/index.js" target="_blank">https://github.com/armed/ordered-read-streams/blob/master/index.js</a></p>
        <p>ordered-read-streams/index.js</p>
        <pre class="hljs-dark"><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-title">OrderedStreams<span class="hljs-params">(streams, options) {
  <span class="hljs-keyword">if (!(<span class="hljs-keyword">this <span class="hljs-keyword">instanceof(OrderedStreams))) {
    <span class="hljs-keyword">return <span class="hljs-keyword">new OrderedStreams(streams, options);
  }
 
  streams = streams || [];
  options = options || {};
 
  <span class="hljs-keyword">if (!<span class="hljs-built_in">Array.isArray(streams)) {
    streams = [streams];
  }
 
  options.objectMode = <span class="hljs-literal">true;
 
  Readable.call(<span class="hljs-keyword">this, options);
 
  <span class="hljs-comment">// stream data buffer
  <span class="hljs-keyword">this._buffs = [];
 
  <span class="hljs-keyword">if (streams.length === <span class="hljs-number">0) {
    <span class="hljs-keyword">this.push(<span class="hljs-literal">null); <span class="hljs-comment">// no streams, close
    <span class="hljs-keyword">return;
  
 
  streams.forEach(<span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-params">(s, i) {
    <span class="hljs-keyword">if (!s.readable) {
      <span class="hljs-keyword">throw <span class="hljs-keyword">new <span class="hljs-built_in">Error(<span class="hljs-string">‘All input streams must be readable‘);
    }
    s.on(<span class="hljs-string">‘error‘, <span class="hljs-function"><span class="hljs-keyword">function <span class="hljs-params">(e) {
      <span class="hljs-keyword">this.emit(<span class="hljs-string">‘error‘, e);
    }.bind(<span class="hljs-keyword">this));
 
    <span class="hljs-keyword">var buff = [];
    <span class="hljs-keyword">this._buffs.push(buff);
 
    s.on(<span class="hljs-string">‘data‘, buff.unshift.bind(buff));
    s.on(<span class="hljs-string">‘end‘, flushStreamAtIndex.bind(<span class="hljs-keyword">this, i));
  }, <span class="hljs-keyword">this);
}
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
        <p>参考:<a href="https://github.com/armed/ordered-read-streams/blob/master/index.js" target="_blank">https://github.com/armed/ordered-read-streams/blob/master/index.js</a></p>
        <h2>写在后面</h2>
        <p>兜兜转转一大圈,终于找到了<code>gulp.src()</code>的源头,大致流程如下,算是蛮深的层级。代码细节神马的,有兴趣的同学可以深究一下。</p>
        <blockquote>
            <p>ordered-read-streams --> glob-stream --> vinyl-fs --> gulp.src()</p>
        </blockquote>
    </body>
</html>

好了,继续安装另一个插件gulp-htmlmin。照着readme安装就是,不过一下安装这么多依赖,黑压压一坨,着实吓人!

然后在xxxx目录下,建立一个gulpfile.js文件,内容直接抄gulp-htmlmin的readme:

var gulp = require(‘gulp‘);
var htmlmin = require(‘gulp-htmlmin‘);
 
gulp.task(‘minify‘, function() {
  gulp.src(‘src/*.html‘)
    .pipe(htmlmin({collapseWhitespace: true}))
    .pipe(gulp.dest(‘dist‘))
});

然后控制台运行gulp命令,报错,说什么“ Task ‘default‘ is not in your gulpfile”。只好求助谷歌,发现这个东西

var gulp   = require(‘gulp‘);
var coffee = require(‘gulp-coffee‘);
  
gulp.task(‘scripts‘, function () {
  gulp.src(‘src/*.coffee‘)
    .pipe(coffee())
    .pipe(gulp.dest(‘./‘));
});
  
gulp.task(‘watch‘, function () {
  gulp.watch(‘src/*.coffee‘, [‘scripts‘]);
});
  
gulp.task(‘default‘, [‘scripts‘, ‘watch‘]);

于是将原来的代码改装一下:

var gulp = require(‘gulp‘);
var htmlmin = require(‘gulp-htmlmin‘);
 
gulp.task(‘minify‘, function() {
  gulp.src(‘src/*.html‘)
    .pipe(htmlmin({collapseWhitespace: true}))
    .pipe(gulp.dest(‘dist‘))
});
gulp.task(‘watch‘, function () {
  console.log(‘继续压死你!‘)
  gulp.watch(‘src/*.html‘, [‘minify‘]);
});
gulp.task(‘default‘, [‘minify‘, ‘watch‘]);

运行gulp命令,生成dest目录,里面的index.html已经成功被压缩。并且有了watch任务,以后我们每次修改html,都会同步到dest中去。

估计default任务应该是类似C语言的main方法那样的东西,没有它是无法带动其他任务的。

接着我们好好学一下其基础吧。

gulp有5个基本方法:src、dest、task、run、watch

gulp.src()

gulp模块的src方法,用于产生数据流。它的参数表示所要处理的文件,一般有以下几种形式:

  • js/app.js:指定确切的文件名
  • js/*.js:某个目录所有后缀名为js的文件
  • js/**/*.js:某个目录及其所有子目录中的所有后缀名为js的文件
  • !js/app.js:除了js/app.js以外的所有文件
  • *.+(js|css):匹配项目根目录下,所有后缀名为js或css的文件

src方法的参数还可以是一个数组,用来指定多个成员:

gulp.src([‘js/**/*.js‘, ‘!js/**/*.min.js‘]); 

gulp.dest()

gulp模块的dest方法,可以用来传送文件,同时写入文件到指定目录。可以重复的发送传递给它的数据,因此可以将文件传送到多个目录中。简单的例子:

gulp.src(‘./client/templates/*.jade‘
    .pipe(jade())
    .pipe(gulp.dest(‘./build/templates‘))
    .pipe(minify())
    .pipe(gulp.dest(‘./build/minified_templates‘));

gulp.task()

gulp模块的task方法,用于定义具体的任务。它的第一个参数是任务名,第二个参数是任务函数。下面是一个非常简单的任务函数:

gulp.task(‘greet‘, function () { 
   console.log(‘Hello world!‘);
});

task方法还可以指定按顺序运行的一组任务:

gulp.task(‘build‘, [‘css‘, ‘js‘, ‘imgs‘]); 

上面代码先指定build任务,它按次序由css、js、imgs三个任务所组成。注意:由于每个任务都是异步调用,所以没有办法保证js任务的开始运行的时间,正好是css任务运行结束时间。

如果希望各个任务严格按次序运行,可以把前一个任务写成后一个任务的依赖模块:

gulp.task(‘css‘, [‘greet‘], function () { 
   // Deal with CSS here
});

上面代码表明,css任务依赖greet任务,所以css一定会在greet运行完成后再运行。

如果一个任务的名字为default,就表明它是“默认任务”,在命令行直接输入gulp命令,就会运行该任务:

gulp.task(‘default‘, function () { 
   // Your default task
});

gulp.run()

gulp模块的run方法,表示要执行的任务。可能会使用单个参数的形式传递多个任务。注意:任务是尽可能多的并行执行的,并且可能不会按照指定的顺序运行:

gulp.run(‘scripts‘,‘copyfiles‘,‘builddocs‘);
 
gulp.run(‘scripts‘,‘copyfiles‘,‘builddocs‘, function(err) { 
    // 所有任务完成,或者触发错误而终止
});

可以使用gulp.run在其他任务中运行任务。也可以在默认任务中使用gulp.run 组织多个更小的任务为一个大任务。

gulp.watch()

gulp模块的watch方法,用于指定需要监视的文件。一旦这些文件发生变动,就运行指定任务:

gulp.task(‘watch‘, function () { 
   gulp.watch(‘templates/*.tmpl.html‘, [‘build‘]);
});

上面代码指定,一旦templates目录中的模板文件发生变化,就运行build任务。

watch方法也可以用回调函数,代替指定的任务:

gulp.watch(‘templates/*.tmpl.html‘, function (event) { 
   console.log(‘Event type: ‘ + event.type);
   console.log(‘Event path: ‘ + event.path);
});

另一种写法是watch方法所监控的文件发生变化时(修改、增加、删除文件),会触发change事件,可以对change事件指定回调函数:

var watcher = gulp.watch(‘templates/*.tmpl.html‘, [‘build‘]);
 
watcher.on(‘change‘, function (event) { 
   console.log(‘Event type: ‘ + event.type);
   console.log(‘Event path: ‘ + event.path);
});

除了change事件,watch方法还可能触发以下事件:

  • end:回调函数运行完毕时触发。
  • error:发生错误时触发。
  • ready:当开始监听文件时触发。
  • nomatch:没有匹配的监听文件时触发。

watcher对象还包含其他一些方法:

  • watcher.end():停止watcher对象,不会再调用任务或回调函数。
  • watcher.files():返回watcher对象监视的文件。
  • watcher.add(glob):增加所要监视的文件,它还可以附件第二个参数,表示回调函数。
  • watcher.remove(filepath):从watcher对象中移走一个监视的文件。

学完这些就可以到其官网上找插件了,毕竟插件才是王道。

转自:http://www.cnblogs.com/rubylouvre/p/4286638.html

时间: 2024-12-14 06:14:46

第一个gulp程序的相关文章

第一个JAVA程序解析

上一篇博客中,我们编写了第一个JAVA程序并使用最原始的方式对其进行编译运行,很多文章或书籍中对该种编译运行方式不做介绍,但是我认为这有助于我们更好的了解.学习JAVA,是不可忽略的一部分,在我身边有很多人,学完一段时间JAVA后,只知道如何在IDE中进行开发,却不知脱离IDE后该如何运行,不得不说这也是一种悲哀. 言归正传,我们来看一下上一篇博客中的例子"Hello World",这也是几乎所有的编程语言都会编写的一个例子,非常的经典. 下面我们就来分析一下: /**  *  我的第

二、第一个ExtJS程序:helloExtJS

开发前的准备 下载并解压ExtJS包后,可以得到下图的文件目录结构: 在实际开发过程中并不需要所有的文件和目录,所需的包含如下目录即可: 若使用eclipse进行开发,只需将上述文件复制到WebRoot目录或其子目录. 开始 新建firstextjs.html 在使用ExtJS之前,需要在页面引入相应的样式和js文件,一般包括的最小集合是这样:ext-all.js,adapter/ext/ext-base.js,locale/ext-lang-zh_CN.js和整个resources目录. ex

当世界上只剩下一个Java程序员

公元2050年,世界上只剩下了一个Java程序员. 你可能要问了,别的人都去哪儿了?原因很简单, Java没落了. 大约在2030年左右,出现了一个叫做X的语言,它既能做系统级开发(操作系统.数据库.编译器),也能做服务器端的开发,手机端,Web端都不在话下. 更为重要的是,这个新的编程语言和人类的自然语言很接近,无论大人小孩,稍微一学,很快就可以来编程.于是排名前100的语言统统消失了, 程序员们都失业了. Java也不例外,这个昔日的霸主在留下了一堆庞大而复杂的系统以后就不见了. Java程

作为一个女程序员,有感而发

下午,在CSDN看到了一篇女程序员的迷茫的贴子,突然有感而发,写下了自己的一些感想,以及为什么我突然会来考教师资格证的奇怪想法(奇怪的想法,这是很多朋友对我的评价). 我是一个女程序员,目前是一个公司的技术架构师,写着项目核的代码,同时管理着一个不大不小的开发团队,今年一开年,大大小小6-7个新项目转到我手上,加上原有的项目维护,就能初步估计这一年又没什么假期了,还必须照顾着下面一群小伙子的心态.做项目,的确很累,特别是对于女性同胞们来讲.但是生活就是这么回事,为了不让IT狂潮把自己给淹没了,必

关于链表的一个小程序

关于链表的一个小程序: /**************************链表*****************************//* 具备功能 *//* 链表按元素位置插入 *//* 链表按元素位置删除 *//* 链表全表遍历 *//* 链表整表创建(头插法) *//* 链表整表创建(尾插法) *//* 链表整表删除 *//**************************链表*****************************/ #include<stdio.h>#in

如何编写一个gulp插件

很久以前,我们在"细说gulp"随笔中,以压缩JavaScript为例,详细地讲解了如何利用gulp来完成前端自动化. 再来短暂回顾下,当时除了借助gulp之外,我们还利用了第三方gulp插件”gulp-uglify”,来达到压缩JavaScript文件的目的. 代码如下: 今儿,我们的重点就是,自己也来实现一个gulp插件. 正文 其实,如果只是单纯地想要编写一个gulp插件不难,可以借助through2或者through-gulp来编写(through-gulp是基于through

使用MyEclipse开发第一个Web程序

MyEclipse环境配置 首先,安装一个MyEclipse,然后进行一些相关的环境配置(Window->Preferences): 比如字体.Formatter等. 也可以从Eclipse中导出配置,然后在MyEclipse中导入. 这里需要特别注意的是两个配置: 1.JSP的打开方式: 选为用编辑器打开: Window->Preferences->General->File Associations 然后在右边窗口选jsp,下面选择MyEclipse JSP Editor,在右

JavaWeb学习系列——第一个JavaWeb程序

Eclipse中新建一个Dynamic Web Project 指定项目名称.依赖环境 勾选生成web.xml选项 更改项目编译输出目录,项目右键 ->properties ->Java Build Path ->source ->Default output folder,修改为JavaWeb01/WebContent/WEB-INF/classes 在WebContent文件夹下新建一个index.jsp文件,修改其代码如下 <%@ page language="

原创观点:还在认为企业或个人只能申请一个小程序吗?

今天第一次体验了整个小程序注册流程,使用的是我自己的这个简单的教程(个人无appid如何进行申请开发者权限,简单操作步骤),教程虽然是我的,我自己却没有真的实践过,真是误人子弟,自己也不一定能知道:今天亲自测试了一下:首先走完邮箱验证: 验证完毕后,开始进入信息登录,这里,有一些关键信息,是本文观点的主要支撑: 请输入管理员的身份证号码,一个身份证号码只能注册5个小程序. 然后填写完毕后,到了第二个关键点: 为了验证你的身份,请用绑定了管理员本人银行卡的微信扫描二维码. 这个点,透露了两个信息,