构建移动网站与APP:ionic移动开发入门与实战 (跨平台移动开发丛书)
上QQ阅读APP看书,第一时间看更新

4.3 Gulp使用简介(可选学)

在本书的2.1.3节已经介绍过Gulp的长处和安装步骤,本节将介绍Gulp的一些基本概念和最常见的使用方法,帮助读者未来选用它的一些自动化处理插件和对Ionic默认生成的Gulp主文件进行定制。图4.2中展现了最常用的几个Gulp插件和其对应的功能。

图4.2 常见的Gulp插件和其对应的功能

4.3.1 Gulp主文件gulpfile.js的执行原理

Gulp需要一个文件作为它的主文件,这个文件被强制规定名称为gulpfile.js。要使用Gulp的时候,在项目的根目录中新建一个文件名为gulpfile.js的文件即可。之后要做的就是在gulpfile.js文件中定义任务了。示例4-2是一个最简单的gulpfile.js文件内容示例,它定义了一个默认的任务。

【示例4-2】在gulpfile.js中定义一个默认任务。

        var gulp = require('gulp');
        gulp.task('default', function(){
      console.log('hello');
        });

【代码解析】代码引入gulp模块后使用它的task()方法定义了名为default的默认任务,该任务被调用执行时将在控制台写入字符串“hello”后退出。

运行Gulp任务只需切换到存放gulpfile.js文件的目录,然后在命令行中执行gulp命令就行了。gulp后面可以加上要执行的任务名,例如gulp task1,如果没有指定任务名,则会执行名为default的默认任务。图4.3为分别使用两种方式执行示例4-2中编写的gulpfile.js运行出的结果。

图4.3 在命令行执行4-2中编写的gulpfile.js运行出的结果

在介绍Gulp的其他函数之前,需要先了解gulpfile.js工作方式。在Gulp中,使用的是Node.js中的流(stream),首先获取到需要的流,然后可以通过流的pipe()方法把流依次导入到各个Gulp插件中,一般最后是把流写入到文件里结束。所以可以把Gulp看作是一个流处理器工具,这样它在中间的处理环节不需要频繁地生成临时文件(比Gulp更早出现的同类工具Grunt是以临时文件方式工作的),效率要更高。

这样可以推想出来,Gulp的模式一般应该是:首先获取到想要处理的文件流(通过gulp.src方法),然后把文件流依次导入到Gulp的各个插件中(通过pipe方法依次包装各个插件方法),最后把经过插件处理后的流再写入到文件里(也是通过pipe方法包装gulp.dest, gulp.dest方法则把流中的内容写入到文件中)。如果省去中间各个插件环节,那么最简单的一个gulpfile.js就写出来了:

【示例4-3】在gulpfile.js中使用流方式执行复制文本文件的操作。

        var gulp = require("gulp");
        gulp.task('default', function(){
          gulp.src('*.txt')
          .pipe(gulp.dest('test_gulp'));
        });

【代码解析】在【示例4-2】的结构里加入了获取gulpfile.js当前目录的所有.txt文件,随后写入到目录底下的test_gulp目录中。

了解了Gulp的工作原理和代码编写结构后,下面的内容将继续讲解Gulp的四个基本API接口: gulp.src、gulp.task、gulp.dest和gulp.watch。

4.3.2 获取流函数src

gulp.src()方法正是用来获取流的,但要注意这个流里的内容不是原始的文件流,而是一个虚拟文件对象流(Vinyl files),这个虚拟文件对象流中存储着原始文件的路径、文件名、内容等信息。其语法为:

        gulp.src(globs [, options]);

1.globs参数

globs参数是文件匹配模式(类似正则表达式),用来匹配文件路径(包括文件名),当然这里也可以直接指定某个具体的文件路径。当有多个匹配模式时,该参数可以为一个数组:类型为String或Array。例如:

        gulp.src(['js/*.js', 'css/*.css', '*.html']); //分别匹配各目录下的js、css和本目录下的html文件

2.options参数

options参数是可选参数对象,以下为常见选项参数:

● options.buffer

类型:Boolean默认值:true

说明:设置为false时将返回file.content的流而不缓冲整个文件的内容,处理大文件时非常有用。

提示

插件可能并不会实现对流的支持。

● options.base

类型:String

说明:显式设置输出路径以某个路径的某个组成部分为基础向后拼接。

假设在一个路径为client/js/somedir的目录中,有一个文件叫somefile.js :

        // 匹配 'client/js/somedir/somefile.js'现在 `base` 的值为 `client/js/`
        gulp.src('client/js/**/*.js')
        // 写入 'build/somedir/somefile.js'将`client/js/`替换为build
        .pipe(gulp.dest('build'));

        // base的值为 'client'
        gulp.src('client/js/**/*.js', { base: 'client' })
        // 写入 'build/js/somedir/somefile.js'将`client`替换为build
        .pipe(gulp.dest('build'));

【代码解析】在路径中的**代表匹配路径中的0个或多个目录及其子目录。使用options.base就能够选择保留原路径里的一些下层目录结构。

4.3.3 写文件函数dest

gulp.dest()方法是用来写入文件或目录的,其语法为:

        gulp.dest(path [, options]);

1.path参数

path参数是写入文件或目录的路径;

2.options参数

options参数是可选参数对象,以下为常见选项参数:

● options.cwd

类型:String默认值:process.cwd()

说明:输出目录的cwd(current working directory当前工作目录)参数,只在所给的输出目录是相对路径时有效。

● options.mode

类型:String默认值: 0777

说明:八进制权限字符,用以定义所有在输出目录中所创建的目录的权限。

这里说明一下生成的文件路径与给gulp.dest()方法传入的路径参数之间的关系。gulp.dest(path)生成的文件路径是传入的path参数后面再加上前面调用gulp.src()中有通配符开始出现的那部分路径。例如:

        var gulp = require('gulp');
        //有通配符开始出现的那部分路径为 **/*.js
        gulp.src('script/**/*.js')
        //最后生成的文件路径为dist/**/*.js,如果 **/*.js匹配到的文件为jquery/jquery.js ,
        //则生成的文件路径为dist/jquery/jquery.js
        .pipe(gulp.dest('dist'));

【代码解析】通过这种在写入路径中保留通配符所匹配出的路径的方式,能保留源目录的结构。

提示

用gulp.dest()把文件流写入文件后,文件流仍然可以继续使用。在后面的4.3.6节我们可以看到Ionic的gulpfile.js利用这一特性同时生成了正常可读的和压缩优化过的CSS文件。

4.3.4 监视文件变化函数watch

gulp.watch()用来监视文件的变化,当文件发生变化后,我们可以利用它来执行相应的任务,例如文件重新压缩生成等。其语法为:

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

1.globs参数

globs参数为要监视的文件匹配模式,规则和用法与4.3.2节里gulp.src()方法中的glob相同。

2.opts参数

opts参数为一个可选的配置对象,通常不需要用到。

3.tasks参数

tasks参数为监视到文件变化后要执行的任务名数组。

例如:

        gulp.task('uglify', function(){
          //do something
        });
        gulp.task('reload', function(){
          //do something
        });
        gulp.watch('js/**/*.js', ['uglify', 'reload']);

【代码解析】在监测到工作目录的js子目录及以下的任何js文件有变动时,则依次调用前面定义过的uglify和reload任务。

4.3.5 定义任务函数task

gulp.task()用来定义任务,在前面几个小节读者应该已经初步接触过它了。其语法为:

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

1.name参数

name代表任务名。

2.deps参数

deps参数是当前定义的任务需要依赖的其他任务名数组。当前定义的任务会在所有依赖的任务执行完毕后才开始执行。如果没有依赖,则可省略这个参数。

3.fn参数

fn参数是任务函数,我们把任务要执行的代码都写在里面,该参数也是可选的。

前面在4.3.1节和4.3.4节已经分别了解了如何定义默认执行的任务和简单的任务,现在介绍当有多个任务时,需要知道怎么通过任务依赖来实现控制任务的执行顺序。例如想要执行one, two, three这三个任务,就可以定义一个空的任务,然后把那三个任务当作这个空的任务的依赖就行了:

        //只要执行default任务,就相当于把one, two, three这三个任务执行了
        gulp.task('default', ['one', 'two', 'three']);

【代码解析】如果任务['one', 'two', 'three']相互之间没有依赖,任务就会按书写的顺序来执行,如果有依赖的话则会先执行依赖的任务。

4.3.6 解析Ionic项目Gulp主文件

了解完Gulp提供的4个基础API接口后,就可以开始通过阅读已有成熟代码学习怎么把Gulp应用到日常工作中了。从本书的主旨出发,笔者做出了一个轻松的决定:通过解析Ionic项目模板自带的Gulp主文件来讲解Gulp的API和一些插件的使用。

读者可以打开在本书前面的2.1.6节生成的Hello Ionic项目目录下的gulpfile.js文件自行查看阅读或是参考示例4-4的代码与注释来学习。

【示例4-4】Ionic项目模板自带的Gulp主文件gulpfile.js

        //引入Gulp库和用到的Gulp插件
        var gulp = require('gulp');
        var gutil = require('gulp-util');
        var bower = require('bower'); //引入Bower库
        var concat = require('gulp-concat');
        var sass = require('gulp-sass');
        var minifyCss = require('gulp-minify-css');
        var rename = require('gulp-rename');
        //引入shelljs库,用于实现Unix shell命令执行
        var sh = require('shelljs');

        //设置项目的SASS文件所在目录
        var paths = {
          sass: ['./scss/**/*.scss']
        };

        //设置默认任务依赖于sass任务
        gulp.task('default', ['sass']);

        //sass任务,将Ionic应用的主SASS文件编译为CSS文件的两种格式
        gulp.task('sass', function(done) {
          // 读取Ionic应用的主SASS文件
          gulp.src('./scss/ionic.app.scss')
            // 编译为CSS文件
            .pipe(sass())
        .on('error', sass.logError)
        //未压缩的版本写入css目录中
            .pipe(gulp.dest('./www/css/'))
            .pipe(minifyCss({ //
              keepSpecialComments: 0
        }))
        //压缩后的版本文件改名
        .pipe(rename({ extname: '.min.css' }))
        //压缩后的版本写入css目录中
        .pipe(gulp.dest('./www/css/'))
        //异步任务完成后执行done通知调用者
            .on('end', done);
        });

        //设置watch任务为监控SASS文件所在目录,如有变化则启动sass任务重新生成css文件
        gulp.task('watch', function() {
          gulp.watch(paths.sass, ['sass']);
        });

        //install任务,调用Bower执行包安装
        gulp.task('install', ['git-check'], function() {
          return bower.commands.install()
            .on('log', function(data) {
              gutil.log('bower', gutil.colors.cyan(data.id), data.message);
            });
        });

        // git-check任务,检查是否安装了git
        gulp.task('git-check', function(done) {
          if (! sh.which('git')) {
            console.log(
              '  ' + gutil.colors.red('Git is not installed.'),
              '\n  Git, the version control system, is required to download Ionic.',
              '\n                                    Download                  git                  here:',
    gutil.colors.cyan('http://git-scm.com/downloads') + '.',
              '\n  Once git is installed, run \'' + gutil.colors.cyan('gulp install')
    + '\' again.'
            );
            process.exit(1);
          }
          done();
        });

【代码解析】代码里最主要的是两个任务:sass和watch。sass作为主任务负责生成css文件,而watch任务将被Ionic CLI调用监控项目sass文件的变化,一旦变化则将在css文件更新后调用浏览器的远程函数重新加载应用。