gulp进阶构建项目由浅入深

阅读目录

gulp基本安装和使用

Gulp的构建过程:gulp是使用nodejs中的stream(流),首先通过gulp.src()方法获取到我们需要的文件流(stream),然后把文件流通过pipe()方法把流导入到gulp的插件中,最后通过插件处理后的流再通过pipe()方法导入到gulp.dest()中,gulp.dest()方法把流中的内容写入到文件中。

1. Gulp安装:

首先我们需要安装nodejs,然后进行全局安装;安装如下:

   sudo npm install gulp –g

全局安装后,还需要切换到项目的根目录下,单独为单个项目进行安装下;安装如下:

   sudo npm install gulp 

如果想在安装的时候把gulp写进package.json文件的依赖中,则可以加上 –save-dev

   sudo npm install –save-dev  gulp

2. 如何使用gulp?

在项目的根目录下新建一个 gulpfile.js文件,之后就可以定义一个任务了;

比如如下简单的任务:代码如下:

var gulp = require('gulp');

gulp.task('default',function(){

    console.log('hello world');

});

现在我项目的目录结构假如是如下样子:

最后我们进行命令行切换到项目的根目录下,运行gulp命令后,就可以在控制台看到consoe.log的打印的消息了;

gulp API介绍

Gulp.src(globs[,options])

最常见的我们使用四个API,gulp.task() gulp.src() gulp.dest() gulp.watch();

该方法是获取我们需要的文件流,这个流里面的内容不是原始的文件流,而是一个虚拟文件对象流(Vinyl files);该方法有2个参数

globs类型是 String 或 Array , 该文件流可以是一个单独的字符串形式,也可以是一个数组形式;

options是一个对象类型;该对象类型有一个我们常用的base字段配置 options.base是经常会使用的到;

比如如下代码:

var gulp = require('gulp');
var uglify = require('gulp-uglify');
gulp.task("uglify-js",function(){
return gulp.src("src/js/*.js")
.pipe(uglify())
.pipe(gulp.dest('build'));
});
gulp.task('default',['uglify-js']);
// 写入到 build/a.js 和 build/index.js

如下目录结构:

我们使用base字段来继续编写如下代码:

var gulp = require('gulp');
var uglify = require('gulp-uglify'); gulp.task("uglify-js",function(){
return gulp.src("src/js/*.js",{ base: 'src' })
.pipe(uglify())
.pipe(gulp.dest('build'));
});
gulp.task('default',['uglify-js']);

我们再在项目的根目录下面运行gulp命令可以看到项目的目录结构变为如下:

因此我们可以理解base字段为相对于路径来进行打包,最后生成 build/js/*.js文件;

看看Gulp用到的glob的匹配规则:

Gulp内部使用了node-glob模块来实现文件匹配功能。该文件匹配类似于JS中的正则表达式;如下匹配:

 匹配文件路径中的0个或者多个字符,但是不会匹配路径分隔符。比如: 可以匹配 abc.js,x.js,aaa,abc/(路径分隔符出现在末尾也可以匹配);但是不能匹配类似于这样的路径分隔符 abc/aa.js

*.* 可以匹配a.xxx; xxxx.yyyy;等

*/*/*.js  可以匹配a/b/c.js,但不是不能匹配 a/b.js 或者 a/b/c/d.js

**  可以匹配路径中的0个或者多个目录及其子目录。比如:能匹配abc,a/b.js,

a/b/c.js,x/y/z等等;

**/*.js  能够匹配a.js , a/a.js,a/aaa/aaaa/a.js等等;也就是说只要以.js结尾的,不管前面有多少个文件或者分隔符都可以匹配;

a/**/z  能匹配a/z,a/b/z, a/b/c/z等等。

a/**b/z  能匹配a/b/z,a/sb/z, 不是不能匹配a/x/y/xxb/z;

?.js  能匹配a.js,b.js,c.js,相当于js正则里面的一样匹配0个或者1个,优先匹配;

[xyz].js 能匹配x.js,y.js,z.js,类似于js正则一样,中括号中的任意一个字符;

[^xyz].js 除了中括号中的x,y,z中的其他的任意一个字符;

当有多种匹配模式的时候可以使用数组,如下:

gulp.src([‘js/*.js’,’css/*.css’]);

我们也可以排除一些文件,可以使用!, 比如如下代码:

gulp.src([‘js/*.js’,’css/*.css’,’!reset.css’]) ; 匹配所有的js/目录下的js文件及匹配css/目录下的css文件,但是不包括reset.css文件;但是不能把排除写在第一个元素位置;

比如如下代码: gulp.src([‘!reset.css’,’css/*.css’]); 这样的是排除不掉的,这种方式我们在css中可以理解为后面的代码覆盖前面的,因此需要写在后面才能排除当前的;

gulp.dest(path[,options])

该方法可以理解为把目标的源文件通过pipe方法导入到gulp插件中,最后把文件流写到目标文件中;如果该文件不存在的话,则会自动创建它;比如上面的gulp.src(),目录结构一刚开始build目录是没有的,通过打包后自动创建build文件夹;

字段path: 文件被写入的路径(输出的目录),也可以传入一个函数,在函数中返回相应的路径。

字段options也是一个对象类型;

Gulp.dest(path) 生成的文件路径是相对于gulp.src()中有通配符开始出现的那部分路径。

比如如下代码:

var gulp = require('gulp');
var uglify = require('gulp-uglify');
gulp.task("uglify-js",function(){
return gulp.src("src/js/*.js")
.pipe(uglify())
.pipe(gulp.dest('build'));
});
gulp.task('default',['uglify-js']);

gulp.src()上面有通配符的是 *.js, 因此最后生成的文件路径是 build/*.js;
但是如果没有出现通配符的情况下,比如如下代码:
gulp.src("src/js/a.js")
.pipe(gulp.dest('build'));

那么最后生成的路径就是 build/a.js 了;

当然我们可以在gulp.src()方法中配置base属性,如果没有配置base属性的话,那么默认生成的路径就是相对于通配符出现的那部分路径;如果设置了base属性的话,那么就相对于base的那个设置的路径;假如现在的源目录结构为src/js/下游很多js文件

比如如下代码:

var gulp = require('gulp');
var uglify = require('gulp-uglify');
gulp.task("uglify-js",function(){
return gulp.src("src/js/*.js",{ base: 'src' })
.pipe(uglify())
.pipe(gulp.dest('build'));
});
gulp.task('default',['uglify-js']);

是相对于src文件下的,因此最后生成的路径为 build/js/*.js

gulp.task(name[,deps],fn);

该方法是定义一个任务;

name: 是任务的名字;

deps: {Array} 类型是数组类型;一个包含任务列表的数组,这些任务会在你当前任务运行之前完成;比如如下代码:

gulp.task('mytask', ['one', 'two', 'task', 'names'], function() {

// 做一些事

});

比如上面的代码,我们想要完成'mytask'这个任务的话,首先会执行依赖数组中的那些任务,但是如果依赖任务里面又使用了异步的方法,比如使用setTimeout这样的时候,那么这个时候,我再执行mytask这个任务的时候,就不会等待该依赖任务完成后再执行了;比如如下代码:

var gulp = require('gulp');
gulp.task('one',function(){
//one是一个异步执行的任务
setTimeout(function(){
console.log('one is done')
},5000);
});
//two任务虽然依赖于one任务,但并不会等到one任务中的异步操作完成后再执行
gulp.task('two',['one'], function(){
console.log('two is done');
});
gulp.task('default',['two']);

上面的例子中我们执行two任务时,会先执行one任务,但不会去等待one任务中的异步操作完成后再执行two任务,而是紧接着执行two任务。所以two任务会在one任务中的异步操作完成之前就执行了。

但是如果我们想等待one任务中的setTimeout执行完成后,再执行two这个任务该怎么办呢?

我们可以使用如下方法,代码如下:

var gulp = require('gulp');
gulp.task('one',function(fn){
// fn 为任务函数提供的回调 用来通知该任务已经完成
//one是一个异步执行的任务
setTimeout(function(){
console.log('one is done');
fn();
},1000);
});
//two任务虽然依赖于one任务,但并不会等到one任务中的异步操作完成后再执行
gulp.task('two',['one'], function(){
console.log('two is done');
});
gulp.task('default',['two']);

gulp.watch(glob[,opts],tasks)

用来监听文件的变化,当文件发生改变的时候,我们可以用它来执行相应的任务;

参数如下:

glob: 为要监听的文件匹配模式,规则和gulp.src中的glob相同;

opts: 为一个可选的配置对象,一般不怎么用;

tasks: 为文件变化后要执行的任务,为一个数组;

比如代码如下:

gulp.task('two', function(){

console.log('two is done');

});

gulp.watch('js/**/*.js',['two'])

gulp一些常用插件

1.gulp-rename(重命名)

用来重命名文件流中的文件。

安装:npm install --save-dev gulp-rename

比如如下代码:

var gulp = require('gulp'),
rename = require('gulp-rename'),
uglify = require("gulp-uglify"); gulp.task('rename', function () {
gulp.src('src/**/*.js')
.pipe(uglify()) //压缩
.pipe(rename('index.min.js'))
.pipe(gulp.dest('build/js'));
});
gulp.task('default',['rename']);
//关于gulp-rename的更多强大的用法请参考https://www.npmjs.com/package/gulp-rename

2.gulp-uglify(JS压缩)

安装:npm install --save-dev gulp-uglify

还是上面的gulpfile.js代码如下:

var gulp = require('gulp'),
rename = require('gulp-rename'),
uglify = require("gulp-uglify"); gulp.task('rename', function () {
gulp.src('src/**/*.js')
.pipe(uglify()) //压缩
.pipe(rename('index.min.js'))
.pipe(gulp.dest('build/js'));
});
gulp.task('default',['rename']);

3.gulp-minify-css(css文件压缩)

安装:npm install --save-dev gulp-minify-css

代码如下:

var gulp = require('gulp'),
minifyCss = require("gulp-minify-css");
gulp.task('minify-css', function () {
gulp.src('src/**/*.css') // 要压缩的css文件
.pipe(minifyCss()) //压缩css
.pipe(gulp.dest('build'));
});
gulp.task('default',['minify-css']);

4.gulp-minify-html(html压缩)

安装:npm install --save-dev gulp-minify-html

代码如下:

var gulp = require('gulp'),
minifyHtml = require("gulp-minify-html"); gulp.task('minify-html', function () {
gulp.src('src/**/*.html') // 要压缩的html文件
.pipe(minifyHtml()) //压缩
.pipe(gulp.dest('build'));
}); gulp.task('default',['minify-html']);

5.gulp-concat(JS文件合并)

安装:npm install --save-dev gulp-concat

代码如下:

var gulp = require('gulp'),
concat = require("gulp-concat"); gulp.task('concat', function () {
gulp.src('src/**/*.js') //要合并的文件
.pipe(concat('index.js')) // 合并匹配到的js文件并命名为 "index.js"
.pipe(gulp.dest('build/js'));
}); gulp.task('default',['concat']);

6.gulp-less (less编译)

安装:npm install –save-dev  gulp-less

Gulpfile.js代码如下:

var gulp = require('gulp'),
less = require("gulp-less"); gulp.task('compile-less', function () {
gulp.src('src/less/*.less')
.pipe(less())
.pipe(gulp.dest('build/css'));
}); gulp.task('default',['compile-less']);

7.gulp-sass(sass编译)

安装:npm install –save-dev  gulp-sass

代码如下:

var gulp = require('gulp'),
sass = require("gulp-sass"); gulp.task('compile-sass', function () {
gulp.src('src/sass/*.sass')
.pipe(sass())
.pipe(gulp.dest('build/css'));
}); gulp.task('default',['compile-sass']);

8.gulp-imagemin(图片压缩)

安装:npm install –save-dev  gulp-imagemin

代码如下:

var gulp = require('gulp');
var imagemin = require('gulp-imagemin'); gulp.task('uglify-imagemin', function () {
return gulp.src('src/images/*')
.pipe(imagemin())
.pipe(gulp.dest('build/images'));
});
gulp.task('default',['uglify-imagemin']);

9.理解 Browserify

browserify是一个使用node支持的CommonJS模块标准 来为浏览器编译模块的,可以解决模块及依赖管理;

先来看看使用gulp常见的问题:

1. 使用 gulp 过程中,偶尔会遇到 Streaming not supported 这样的错误。这通常是因为常规流与 vinyl 文件对象流有差异、
gulp 插件默认使用了只支持 buffer (不支持 stream)的库。比如,不能把 Node 常规流直接传递给 gulp 及其插件。
比如如下代码:会抛出异常的;

var gulp = require('gulp');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var fs = require('fs'); gulp.task('bundle', function() {
return fs.createReadStream('./test.txt')
.pipe(uglify())
.pipe(rename('bundle.min.js'))
.pipe(gulp.dest('dist/'));
});
gulp.task('default',['bundle']);

gulp 选择默认使用内容转换成 buffer 的 vinyl 对象流,以方便处理。当然,设置 buffer: false 选项,可以让 gulp 禁用 buffer:
比如如下gulpfile.js代码如下:

var gulp = require('gulp');
var fs = require('fs');
gulp.task('bundle', function() {
return gulp.src('./src/js/app.js', {buffer: false}).on('data', function(file) {
var stream = file.contents;
stream.on('data', function(chunk) {
console.log('Read %d bytes of data', chunk.length);
});
});
})
gulp.task('default',['bundle']);

运行如下:

2. Stream 和 Buffer 之间转换

基于依赖的模块返回的是 stream, 以及 gulp 插件对 stream 的支持情况,有时需要把 stream 转换为 buffer。比如很多插件只支持 buffer,如 gulp-uglify、使用时可以通过 gulp-buffer 转换:Stream转换Buffer 
如下gulpfile.js代码:

var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('gulp-buffer');
var uglify = require('gulp-uglify');
var fs = require('fs');
gulp.task('bundle', function() {
return fs.createReadStream('./src/js/app.js')
.pipe(source('app.min.js')) // 常规流转换成 vinyl 对象
.pipe(buffer())
.pipe(uglify())
.pipe(gulp.dest('dist/'));
})
gulp.task('default',['bundle']);

如下所示:

3. 从 Buffer 到 Stream之间转换

也可以通过使用 gulp-streamify(https://www.npmjs.com/package/gulp-streamify) 或者 gulp-stream (https://www.npmjs.com/package/gulp-stream)插件,让只支持 buffer 的插件直接处理 stream。
如下gulpfile.js代码:

var gulp = require('gulp');
var wrap = require('gulp-wrap');
var streamify = require('gulp-streamify');
var uglify = require('gulp-uglify');
var gzip = require('gulp-gzip'); gulp.task('bundle', function() {
return gulp.src('./src/js/app.js', {buffer: false})
.pipe(wrap('(function(){<%= contents %>}());'))
.pipe(streamify(uglify()))
.pipe(gulp.dest('dist'))
.pipe(gzip())
.pipe(gulp.dest('dist'));
});
gulp.task('default',['bundle']);

如下所示:

4. 使用browserify进行Stream 向 Buffer 转换

vinyl-source-stream + vinyl-buffer
vinyl-source-stream(https://www.npmjs.com/package/vinyl-source-stream) : 将常规流转换为包含 Stream 的 vinyl 对象;
vinyl-buffer(https://www.npmjs.com/package/vinyl-buffer) 将 vinyl 对象内容中的 Stream 转换为 Buffer。

gulpfile.js代码如下:

var browserify = require('browserify');
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer'); gulp.task('browserify', function() {
return browserify('./src/js/app.js')
.bundle()
.pipe(source('bundle.js')) // gives streaming vinyl file object
.pipe(buffer()) // convert from streaming to buffered vinyl file object
.pipe(uglify())
.pipe(gulp.dest('./dist/js'));
});
gulp.task('default',['browserify']);

vinyl-source-stream 使用指定的文件名bundle.js创建了一个 vinyl 文件对象实例,因此可以不再使用 gulp-rename(gulp.dest 将用此文件名写入结果)。

如下所示:

5. 使用browserify多文件操作

    5-1. 使用Gulp和Browserify单个文件操作也可以如下:

var gulp = require('gulp');
var browserify = require('browserify');
var source = require('vinyl-source-stream'); gulp.task('browserify', function(){
return browserify(
{entrieis:['./src/js/app.js']})
.bundle()
.pipe(source("bundle.js"))
.pipe(gulp.dest('dist'));
});
gulp.task('default',['browserify']);

5-2 多文件操作如下:

var gulp       = require('gulp');
var source = require('vinyl-source-stream');
var browserify = require('browserify');
var glob = require('glob');
var es = require('event-stream'); gulp.task('browserify', function(done) {
glob('./src/**/*.js', function(err, files) {
if(err) {
done(err)
};
var tasks = files.map(function(entry) {
return browserify({ entries: [entry] })
.bundle()
.pipe(source(entry))
.pipe(gulp.dest('./dest'));
});
es.merge(tasks).on('end', done);
})
});
gulp.task('default',['browserify']);

5-3 也可以使用gulp.src和browserify一起使用;代码如下:

var gulp       = require('gulp');
var source = require('vinyl-source-stream');
var browserify = require('browserify');
var glob = require('glob');
var es = require('event-stream');
var buffer = require('vinyl-buffer'); gulp.task('browserify', function(done) {
gulp.src('./src/**/*.js',function(err,files) {
if(err) {
done(err)
}
files.forEach(function(file){
return browserify({ entries: [file] })
.bundle()
.pipe(source(file))
.pipe(buffer())
.pipe(gulp.dest('./dest'));
});
});
});
gulp.task('default',['browserify']);

browserify深入学习;

1.前言:
之前我们做项目的时候,比如需要jquery框架的话,我们可能需要下载jquery源码,然后引入到我们的项目中,之后在html文件中像如下引入即可:
<script src="path/to/jquery.js"></script>

2.bower学习
之后我们学习了 Bower,因此我们安装了Bower,然后进入我们的项目文件根目录中 在命令行中使用bower安装jquery;如下命令:
bower install jquery
之后会在我们的根目录中生成一个 bower_components文件,里面包含了jquery文件,因此我们需要在我们的html文件中这样引入jquery了;
<script src="bower_components/jquery/dist/jquery.js"></script>

如下所示:

3. npm&Browserify学习
我们现在又可以使用 命令行用npm安装jQuery。进入项目的根目录后,运行如下命令:
npm install --save-dev jquery

接着我们在命令行中全局安装 browserify;命令如下:
sudo npm install -g browserify
现在我们就可以在命令行中使用 browserify命令了;
比如现在我在我的项目目录下的源文件 src/js/a.js 下想要使用jquery的话,我们可以在a.js代码如下引用:

var $ = require('jquery');
$(function(){
// 获取页面中的DOM元素
console.log($("#jquery2"));
});
function a() {
console.log("a.js");
}
a();

再进入命令行相对应的js文件中,进行如下命令:
browserify a.js -o dest.js
执行命令后会在同目录下生成dest.js,该文件包含jquery.js和a.js;然后我们把dest文件引入到我们需要的html文件中即可访问;

4. gulp和Browserify 一起使用
结合gulp一起使用时,我们只需要把Browserify安装到我们的项目内即可;因此进入项目的根目录中,进行如下命令安装:
npm install --save-dev browserify

然后在项目的根目录中在gulpfile.js文件中加入如下代码:

var gulp = require("gulp");
var browserify = require("browserify");
var sourcemaps = require("gulp-sourcemaps");
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer'); gulp.task("browserify", function () {
var b = browserify({
entries: "./src/js/a.js",
debug: true
});
return b.bundle()
.pipe(source("bundle.js"))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(sourcemaps.write("."))
.pipe(gulp.dest("./dist"));
}); gulp.task('default',['browserify']);

a.js代码还是如下:

var $ = require('jquery');
$(function(){
// 获取页面中的DOM元素
console.log($("#jquery2"));
});
function a() {
console.log("a.js");
}
a();

进入项目的根目录中运行命令 gulp即可,在目录中会生成dist目录(包含bundle.js和bundle.js.map)两个文件;之后在html文件页面上引入
dist目录文件下的bundle.js即可;

在上面的代码中,debug: true是告知Browserify在运行同时生成内联sourcemap用于调试。
如果我们把debug设置成false的话;在浏览器中访问页面,可以看到如下:

如果我们把debug设置成true的话,在浏览器中访问页面,可以看到如下:

引入gulp-sourcemaps并设置loadMaps: true是为了读取上一步得到的内联sourcemap,并将其转写为一个单独的sourcemap文件。
如果我们把loadMaps设置成false的话,我们在浏览器访问页面如下图所示:

如果我们把loadMaps设置成true的话,我们在浏览器访问页面如下图所示:

vinyl-source-stream用于将Browserify的bundle()的输出转换为Gulp可用的[vinyl][](一种虚拟文件格式)流。
vinyl-buffer用于将vinyl流转化为buffered vinyl文件(gulp-sourcemaps及大部分Gulp插件都需要这种格式)。

如果代码比较多,可能一次编译需要很长时间。这个时候,我们可以使用[watchify][]。它可以在你修改文件后,
只重新编译需要的部分(而不是Browserify原本的全部编译),这样,只有第一次编译会花些时间,此后的即时变更刷新则十分迅速。
如下代码:

var watchify = require('watchify');
var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var gutil = require('gulp-util');
var sourcemaps = require('gulp-sourcemaps');
var assign = require('lodash.assign'); // 在这里添加自定义 browserify 选项
var customOpts = {
entries: ['./src/js/a.js'],
debug: true
};
var opts = assign({}, watchify.args, customOpts);
var b = watchify(browserify(opts)); // 在这里加入变换操作
// 比如: b.transform(coffeeify); gulp.task('js', bundle); // 这样你就可以运行 `gulp js` 来编译文件了
b.on('update', bundle); // 当任何依赖发生改变的时候,运行打包工具
b.on('log', gutil.log); // 输出编译日志到终端 function bundle() {
return b.bundle()
// 如果有错误发生,记录这些错误
.on('error', gutil.log.bind(gutil, 'Browserify Error'))
.pipe(source('bundle.js'))
// 可选项,如果你不需要缓存文件内容,就删除
.pipe(buffer())
// 可选项,如果你不需要 sourcemaps,就删除
.pipe(sourcemaps.init({loadMaps: true})) // 从 browserify 文件载入 map
// 在这里将变换操作加入管道
.pipe(sourcemaps.write('./')) // 写入 .map 文件
.pipe(gulp.dest('./dist'));
}
gulp.task('default',['js']);

5. 使用Browserify来组织JavaScript文件

还是上面那个项目,假如src/js文件内有2个js文件,分别为a.js和b.js;假如现在a.js想引用b.js的模块,就像seajs那样通过require来引用如何做?
现在我们可以在b.js这样编写代码;把我们的代码模块通过module.exports 或 exports模块对外提供接口,和其他的比如seajs一样编写代码
即可:比如现在b.js代码如下:
function b() {
    console.log("b.js");
}
module.exports = b;
那么a.js代码如下:
var b = require('./b');
function a() {
   b();
   console.log("a.js");
}
a();
然后再在gulpfile.js文件代码还是如下:

var gulp = require("gulp");
var browserify = require("browserify");
var sourcemaps = require("gulp-sourcemaps");
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer'); gulp.task("browserify", function () {
var b = browserify({
entries: "./src/js/a.js",
debug: true
});
return b.bundle()
.pipe(source("bundle.js"))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(sourcemaps.write("."))
.pipe(gulp.dest("./dist"));
}); gulp.task('default',['browserify']);

在命令行中运行gulp,即可生成bundle.js文件;引用该文件即可解决模块依赖的问题;我们打开bundle.js文件查看代码如下:

(function e(t,n,r){
/*
function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;
if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");
throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];
return s(n?n:e)},l,l.exports,e,t,n,r)}
return n[o].exports}var i=typeof require=="function"&&require;
for(var o=0;o<r.length;o++)s(r[o]);return s */ })({1:[function(require,module,exports){ var b = require('./b'); function a() {
b();
console.log("a.js");
}
a();
},{"./b":2}],2:[function(require,module,exports){
function b() {
console.log("b.js");
}
module.exports = b;
},{}]},{},[1])

该函数有3个参数,
第一个参数是一个对象;第二个参数是一个空对象{};第三个参数是一个[1];
第一个参数是一个object;它的每一个key都是一个数字,作为模块的id,每一个数字key对应的值是长度为2的数组。可以看下,第一个key数字1
模块中的数组中的第一个元素是a.js代码;数组中第二个元素是a模块的依赖项,第二个key数字2模块中数组第一个元素是b.js代码;数组中的第二个
元素是空对象{};因为b模块没有依赖项;因此为{};
我们的文件代码通过一个匿名函数被包装起来,这样做的好处是:我们的浏览器中并没有require这样的解决依赖的东西,但是我们又想像seajs,
requireJS等一样使用require来引入文件解决模块依赖的文件的时候,因此 Browserify实现了require、module、exports这3个关键字。
第2个参数几乎总是空的{}。它如果有的话,也是一个模块map;
第3个参数是一个数组,指定的是作为入口的模块id。a.js是入口模块,它的id是1,所以这里的数组就是[1]。
那么 Browserify是如何实现了require、module、exports这3个关键字的呢?
我们前面被注释的代码将解析require、module、exports这三个3个参数,然后让一切运行起来。
这段代码是一个函数,来自于browser-pack项目的[prelude.js][]。

上面我们看到在Browserify打包文件的时候,它会自动使用匿名函数进行包装;因此我们在编写代码的时候一般可以不需要考虑全局变量的问题了;
不需要在函数中添加像类型匿名函数的结构 (function(){})();

10.理解gulp.watch()的使用

gulp.watch()方法可以监听文件的动态修改,它接受一个glob或者glob数组(和gulp.src()一样)以及一个任务数组来执行回调。下面我们来看下
gulp.watch()方法的使用;比如现在gulpfile.js任务代码如下:

var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify'); var paths = {
scripts: ['src/js/**/*.js'],
css: ['src/css/**/*.css'],
// 把源文件html放在src下,会自动打包到指定目录下
html: ['src/html/**/*.html']
}; gulp.task('scripts', function() { return gulp.src(paths.scripts)
.pipe(concat('all.js'))
.pipe(uglify())
.pipe(gulp.dest('build/js'));
}); gulp.task('css', function() { return gulp.src(paths.css)
.pipe(concat('all.css'))
.pipe(gulp.dest('build/css'));
}); // 监听html文件的改变
gulp.task('html',function(){
return gulp.src(paths.html)
.pipe(gulp.dest('html/'));
}); // Rerun the task when a file changes
gulp.task('watch', function() {
gulp.watch(paths.scripts, ['scripts']);
gulp.watch(paths.css, ['css']);
gulp.watch(paths.html, ['html']);
}); // The default task (called when you run `gulp` from cli)
gulp.task('default', ['scripts', 'css', 'html','watch']);

监听src文件下的js和css及html的文件的变化,我们在相关的项目根目录命令行中运行gulp后,当我们改变css或者js文件或html的时候,
可以监听文件的动态修改,因此保存刷新浏览器即可生效,这是gulp-watch的基本功能;如下所示:

会把src下的文件css和js自动打包到build下,src下的html文件会打包到项目根目录下的html文件下;
上面的gulp.watch 回调函数有一个包含触发回调函数信息的event对象:比如我们把gulp watch任务改成如下:
当每次更改文件的时候 都会触发change事件;代码改为如下:

gulp.task('watch', function() {
var watch1 = gulp.watch(paths.scripts, ['scripts']);
var watch2 = gulp.watch(paths.css, ['css']);
var watch3 = gulp.watch(paths.html, ['html']); watch1.on('change', function (event) {
console.log('Event type: ' + event.type); // Event type: changed
console.log('Event path: ' + event.path); // Event path: /Users/tugenhua/gulp/src/css/a.css
});
watch2.on('change', function (event) {
console.log('Event type: ' + event.type); // Event type: changed
console.log('Event path: ' + event.path); // Event path: /Users/tugenhua/gulp/src/css/a.css
});
watch3.on('change', function (event) {
console.log('Event type: ' + event.type); // Event type: changed
console.log('Event path: ' + event.path); // Event path: /Users/tugenhua/gulp/src/css/a.css
});
});

除了change事件,还可以监听很多其他的事件:
end 在watcher结束时触发
error 在出现error时触发
ready 在文件被找到并正被监听时触发
nomatch 在glob没有匹配到任何文件时触发

Watcher对象也包含了一些可以调用的方法:
watcher.end() 停止watcher(以便停止执行后面的任务或者回调函数)
watcher.files() 返回watcher监听的文件列表
watcher.add(glob) 将与指定glob相匹配的文件添加到watcher
watcher.remove(filepath) 从watcher中移除个别文件

上面是通过gulp-watch来动态监听html,css和js文件的改变,但是需要重新刷新页面才能生效;

11.理解LiveReload插件的使用

该插件的作用是当文件被修改的时候,它能实时刷新网页,这样的话就不需要我们实时刷新了;但是该插件需要在我们服务器下生效;因此
我们需要使用 gulp-connect 创建一个服务器;下面是gulpfile.js代码如下;使用liveReload实现实时刷新;

var gulp = require('gulp');
var connect = require('gulp-connect');
var uglify = require("gulp-uglify");
var concat = require("gulp-concat");
var mincss = require("gulp-minify-css");
//自动刷新
var livereload = require("gulp-livereload"); /* 设置路径 */
var paths = {
src : "src/",
css : "src/css/",
scripts : "src/js/",
scss : "src/scss/",
img : "src/images/",
html : "src/html/",
build : "build"
}
// 创建一个webserver 服务器
gulp.task('webserver', function() {
connect.server({
port: 8000,
livereload: true
});
}); gulp.task('scripts', function() { return gulp.src(paths.scripts+ "**/*.js")
.pipe(concat('all.js'))
.pipe(uglify())
.pipe(gulp.dest(paths.build + '/js'));
}); gulp.task('css', function() { return gulp.src(paths.css+ "**/*.css")
.pipe(concat('all.css'))
.pipe(mincss())
.pipe(gulp.dest(paths.build + '/css'));
}); // 监听html文件的改变
gulp.task('html',function(){
return gulp.src(paths.html + "**/*.html")
.pipe(gulp.dest('html/'));
}); //reload server
gulp.task('reload-dev',['scripts','css','html'],function() {
return gulp.src(paths.src + '**/*.*')
.pipe(connect.reload());
}); // Watch
gulp.task('watch', function() {
//监听生产环境目录变化
gulp.watch(paths.src + '**/*.*',['reload-dev']);
}) gulp.task('default', ['webserver','reload-dev','watch']);

12.理解browser-sync的使用

BroserSync在浏览器中展示变化的功能与LiveReload非常相似,但是它有更多的功能。实现静态服务器,也是能实时刷新页面的;BrowserSync也可以在不同浏览器之间同步点击翻页、表单操作、滚动位置等功能。
安装如下命令:
npm install --save-dev browser-sync
如下gulpfile文件是动态监听js,css和html文件的变化实时更新;如下代码:

var gulp = require('gulp');
var connect = require('gulp-connect');
var uglify = require("gulp-uglify");
var concat = require("gulp-concat");
var mincss = require("gulp-minify-css");
//自动刷新
var browserSync = require('browser-sync').create();
var reload = browserSync.reload; /* 设置路径 */
var paths = {
src : "src/",
css : "src/css/",
scripts : "src/js/",
scss : "src/scss/",
img : "src/images/",
html : "src/html/",
build : "build"
} gulp.task('scripts', function() { return gulp.src(paths.scripts+ "**/*.js")
.pipe(concat('all.js'))
.pipe(uglify())
.pipe(gulp.dest(paths.build + '/js'))
.pipe(reload({stream:true})); // inject into browsers
}); gulp.task('css', function() { return gulp.src(paths.css+ "**/*.css")
.pipe(concat('all.css'))
.pipe(mincss())
.pipe(gulp.dest(paths.build + '/css'))
.pipe(reload({stream:true})); // inject into browsers
}); // 监听html文件的改变
gulp.task('html',function(){
return gulp.src(paths.html + "**/*.html")
.pipe(gulp.dest('html/'))
.pipe(reload({stream:true})); // inject into browsers
}); // 创建本地服务器,并且实时更新页面文件
gulp.task('browser-sync', ['scripts','css','html'],function() {
var files = [
'**/*.html',
'**/*.css',
'**/*.js'
]; browserSync.init(files,{
server: {
//baseDir: "./html"
}
}); }); //gulp.task('default', ['webserver','reload-dev','watch']);
gulp.task('default', ['browser-sync'], function () {
gulp.watch("**/*.css", ['css']);
gulp.watch("**/*.html", ['html']);
gulp.watch("**/*.js", ['scripts']);
});

对 browser-sync 更多的学习 请看文档(http://www.browsersync.cn/docs/api/)

gulp构建小型项目的基本过程

比如我现在一个小项目的基本架构如下所示:

src文件夹:是源文件的目录结构;build文件夹是通过构建后生成的文件;

src存放文件如下:

common(该目录是存放公用的插件css文件和js文件)

html目录是存放目前的html文件

images目录存放所有在项目中用到的图片

js目录是在项目中用到的所有的js文件;

less文件是存放需要预编译成css文件;

这上面几个目录都会通过gulpfile.js打包到build目录下;通过上面的学习browserify(可以解决js的模块化依赖问题)及学习 browserSync(实现自动刷新效果),因此目前该项目打包有2个优点:

1. 可以使用require,exports,和moudle这三个参数实现js模块化组织及加载的问题,它不需要依赖于seajs或者requireJS;

2. 可以实时监听html,css,js文件的修改,从而不需要刷新页面,可以提高工作效率;

现在我把package.json用到的依赖包放到下面来:

{
"name": "testProject",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"browser-sync": "^2.12.10",
"browserify": "^13.0.1",
"event-stream": "^3.3.2",
"glob": "^7.0.3",
"gulp": "^3.9.1",
"gulp-buffer": "0.0.2",
"gulp-clean": "^0.3.2",
"gulp-concat": "^2.6.0",
"gulp-connect": "^4.0.0",
"gulp-gzip": "^1.3.0",
"gulp-imagemin": "^3.0.1",
"gulp-less": "^3.1.0",
"gulp-livereload": "^3.8.1",
"gulp-marked": "^1.0.0",
"gulp-minify-css": "^1.2.4",
"gulp-rename": "^1.2.2",
"gulp-sourcemaps": "^1.6.0",
"gulp-str-replace": "0.0.4",
"gulp-streamify": "^1.0.2",
"gulp-uglify": "^1.5.3",
"gulp-util": "^3.0.7",
"gulp-watch": "^4.3.6",
"gulp-wrap": "^0.13.0",
"lodash.assign": "^4.0.9",
"node-glob": "^1.2.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.7.0"
}
}

项目用到的话,直接npm install 就可以把所有的包加载进来;

gulpfile.js代码如下:

var gulp = require('gulp');
var less = require('gulp-less');
var mincss = require('gulp-minify-css');
var concat = require("gulp-concat");
var uglify = require("gulp-uglify");
var clean = require('gulp-clean'); var browserify = require("browserify");
var sourcemaps = require("gulp-sourcemaps");
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer'); var replace = require('gulp-str-replace');
var imagemin = require('gulp-imagemin'); //自动刷新
var browserSync = require('browser-sync').create();
var reload = browserSync.reload; var fs = require('fs');
var fileContent = fs.readFileSync('./package.json');
var jsonObj = JSON.parse(fileContent); var argv = process.argv.pop();
var DEBUGGER = (argv === "-D" || argv === "-d") ? true : false; /* 基础路径 */
var paths = {
css : 'src/common/css/',
less : 'src/less/',
scripts : "src/js/",
img : "src/images/",
html : "src/html/",
build : "build",
src : 'src'
}
var resProxy = "项目的真实路径";
var prefix = "项目的真实路径"+jsonObj.name; if(DEBUGGER) {
resProxy = "http://localhost:3000/build";
prefix = "http://localhost:3000/build";
} // 先清理文件
gulp.task('clean-css',function(){
return gulp.src(paths.build + "**/*.css")
.pipe(clean());
});
gulp.task('testLess', ['clean-css'],function () {
return gulp.src([paths.less + '**/*.less',paths.css+'**/*.css'])
.pipe(less())
.pipe(concat('index.css'))
.pipe(mincss())
.pipe(replace({
original : {
resProxy : /\@{3}RESPREFIX\@{3}/g,
prefix : /\@{3}PREFIX\@{3}/g
},
target : {
resProxy : resProxy,
prefix : prefix
}
}))
.pipe(gulp.dest(paths.build + "/css"))
.pipe(reload({stream:true}));
}); // 监听html文件的改变
gulp.task('html',function(){
return gulp.src(paths.html + "**/*.html")
.pipe(replace({
original : {
resProxy : /\@{3}RESPREFIX\@{3}/g,
prefix : /\@{3}PREFIX\@{3}/g
},
target : {
resProxy : resProxy,
prefix : prefix
}
}))
.pipe(gulp.dest(paths.build+'/html'))
.pipe(reload({stream:true}));
});
// 对图片进行压缩
gulp.task('images',function(){
return gulp.src(paths.img + "**/*")
.pipe(imagemin())
.pipe(gulp.dest(paths.build + "/images"));
});
// 创建本地服务器,并且实时更新页面文件
gulp.task('browser-sync', ['testLess','html','browserify'],function() {
var files = [
'**/*.html',
'**/*.css',
'**/*.less',
'**/*.js'
];
browserSync.init(files,{ server: {
//baseDir: "./html"
} });
}); // 解决js模块化及依赖的问题
gulp.task("browserify",function () {
var b = browserify({
entries: ["./src/js/index.js"],
debug: true
});
return b.bundle()
.pipe(source("index.js"))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(gulp.dest("./build/js"))
.pipe(uglify())
.pipe(sourcemaps.write("."))
.pipe(replace({
original : {
resProxy : /\@{3}RESPREFIX\@{3}/g,
prefix : /\@{3}PREFIX\@{3}/g
},
target : {
resProxy : resProxy,
prefix : prefix
}
}))
.pipe(gulp.dest("./build/js"))
.pipe(reload({stream:true}));
}); gulp.task('default',['testLess','html','images','browserify'],function () {
gulp.watch(["**/*.less","**/*.css"], ['testLess']);
gulp.watch("**/*.html", ['html']);
gulp.watch("**/*.js", ['browserify']);
}); gulp.task('server', ['browser-sync','images'],function () {
gulp.watch(["**/*.less","**/*.css"], ['testLess']);
gulp.watch("**/*.html", ['html']);
gulp.watch("**/*.js", ['browserify']);
});

如果我们在命令行中 运行gulp server -d  那就是开发环境,会自动打开一个服务器;如果运行gulp的话,就是线上的正式环境了;代码会通过replace插件替换成线上的环境;

使用replace插件的好处可以这样引入文件

<script src="@@@PREFIX@@@/js/index.js"></script>
<link rel="stylesheet" href="@@@PREFIX@@@/css/index.css"/>

所有的图片都可以使用这样的@@@PREFIX@@@来引入的,这样的话就可以指向本地的环境和线上的环境了;方便开发;

git项目demo

gulp进阶构建项目由浅入深的更多相关文章

  1. webpack进阶构建项目(一)

    webpack进阶构建项目(一) 阅读目录 1.理解webpack加载器 2.html-webpack-plugin学习 3.压缩js与css 4.理解less-loader加载器的使用 5.理解ba ...

  2. gulp插件构建项目 压缩js、css、image、zip、web服务、跨域等插件

    推荐一个很好文: https://github.com/lin-xin/blog/issues/2 匹配符 *.**.!.{} gulp.src('./js/*.js') // * 匹配js文件夹下所 ...

  3. [转]webpack进阶构建项目(一)

    阅读目录 1.理解webpack加载器 2.html-webpack-plugin学习 3.压缩js与css 4.理解less-loader加载器的使用 5.理解babel-loader加载器 6.理 ...

  4. webpack进阶构建项目(一):1.理解webpack加载器

    1.理解webpack加载器 webpack的设计理念,所有资源都是“模块”,webpack内部实现了一套资源加载机制,这与Requirejs.Sea.js.Browserify等实现有所不同. We ...

  5. 使用gulp自动构建项目

    网址:https://segmentfault.com/a/1190000011514257

  6. gulp 自动化构建html项目--自动刷新

    使用gulp自动化构建项目是前端学习的重要部分,gulp依赖于node.js.首选电脑要配置node和npm. 查看node版本号 node --version 查看npm 版本 npm --vers ...

  7. 基于Gulp + Browserify构建es6环境下的自动化前端项目

    随着React.Angular2.Redux等前沿的前端框架越来越流行,使用webpack.gulp等工具构建前端自动化项目也随之变得越来越重要.鉴于目前业界普遍更流行使用webpack来构建es6( ...

  8. 【gulp】gulp + browsersync 构建前端项目自动化工作流

    什么是 gulp? gulp.js 是一个自动化构建工具,开发者可以使用它在项目开发过程中自动执行常见任务.gulp.js 是基于 node.js 构建的,利用 node.js 流的威力,你可以快速构 ...

  9. gulp自动化构建工具

    gulp    自动化构建工具,实时监控.代码合并.压缩... http://www.gulpjs.com.cn/     中文网 http://gulpjs.com/plugins/     英文网 ...

随机推荐

  1. 保存密码(KeyChain的使用)

    1.导入框架Security.framework 2.编写工具类 /* 该工具类只能保存一个用户和密码 */ /* service 一般为 bundle ID */ @interface GLKeyC ...

  2. jquery的$

    <!DOCTYPE HTML> <HTML> <head> <title>aa</title> <script type=" ...

  3. maven 的 scope的含义

    依赖范围控制哪些依赖在哪些classpath 中可用,哪些依赖包含在一个应用中.让我们详细看一下每一种范围: compile (编译范围) compile是默认的范围:如果没有提供一个范围,那该依赖的 ...

  4. Python 简介和入门

    1.Python简介 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC ...

  5. Google Guava vs Apache Commons for Argument Validation

    It is an established good practice to validate method arguments at the beginning of the method body. ...

  6. 如何在Visual Studio 2013中使用Ribbon For WPF

    1.首先需要 下载Ribbon For WPF.目前最新的版本是Microsoft Ribbon for WPF October 2010. 下载 链接: https://www.microsoft. ...

  7. JavaWeb---总结(四)Tomcat服务器学习和使用(二)

    一.打包JavaWeb应用 在Java中,使用"jar"命令来对将JavaWeb应用打包成一个War包,jar命令的用法如下: 范例:将JavaWebDemoProject这个Ja ...

  8. linux第一面

    随着Linux应用的扩展许多朋友开始接触Linux,根据学习Windwos的经验往往有一些茫然的感觉:不知从何处开始学起.作为一个 Linux系统管理员,我看了许多有关Linux的文档和书籍,并为学习 ...

  9. Altium Designer 多个输出相连等问题报错解决方法

    问题: Altium Designer软件中,项目编译时,Message面板出现,如下错误: Net NetR121_1 contains multiple Output Pins; PC10 con ...

  10. 9.25 DOM作业

    一<style type="text/css">*{margin:0px auto; padding:0px; font-family:微软雅黑; font-size: ...