• gulp进阶构建项目由浅入深


    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

  • 相关阅读:
    让ios支持openssl
    数组
    NSValue
    音频
    NSObject分类
    NSJSONSerialization
    Java并发编程-深入探讨synchronized实现原理
    设计模式-挖掘工厂模式应用场景-全面分析工厂模式原理
    Spring插件安装
    Java并发编程-深入Java同步器AQS原理与应用-线程锁必备知识点
  • 原文地址:https://www.cnblogs.com/tugenhua0707/p/5562548.html
Copyright © 2020-2023  润新知