Gulp简介
Gulp是构建在nodejs上的前端构建工具。就构建这个功能来说,Gulp其实几乎什么也做不了,但是依托它庞大的生态,配合各种各样的插件,gulp在前端构建方面几乎无所不能。
这里简单列举一下gulp可以做到的事情:
- css方面
- css多个合并
- css压缩
- css 自动处理厂商私有前缀
- 预编译css语言:sass,scss,less编译
- JS方面
- HTML
- 压缩
- 插入特定代码片段,比如公有header和footer
- 模块化
- 基于webpack、jspm、browerify的gulp插件实现
- 强缓存
当然,这些并不是Gulp可以做到的全部。但是确实是最常见的部分。依托Gulp的watch方法,我们可以实时监控文件改动进行任务处理+实时刷新。这有效提高了前端开发的效率。
Gulp的安装
gulp安装需要依赖nodejs,这里不解释如何安装nodejs了。
需要说明的是,npm3对早期的npm2依赖无限嵌套这种行为做了平坦化处理,这样有效提高了依赖不会无限深,也有效提高了安装依赖的速度,建议安装之。
下面安装gulp
一切就这样简单.
Gulp的接口
Gulp的接口不多,因为因为其流的思想,用不上太多。
Gulp构建过程,可以分为3步:入口指定->文件留处理->目标位置指定。
具体来说Gulp大致有4个API:
- gulp.task()
- gulp.src()
- gulp.dest()
- gulp.watch()
Gulp.src(globs[, options])
gulp.src是一切任务起点,用来指定入口文件,也就是要用来处理的目标文件
globs 字符串或者数组
常见的globs用法
1 2 3 4 5 6 7 8 9 10 11 12 13
| gulp.src('src/a.scss')
gulp.src('src/*.scss')
gulp.src('src/**/*.scss')
gulp.src([ 'src/a.js', 'src/b.js', '!src/c.js', 'src/d.js' ]);
|
option
option暂时略过,貌似从来没有用过,就不介绍了
gulp.dest(path[, options])
gulp.dist是一切任务的终点,指定处理好的文件存放到哪儿去。
path
path可以是静态的字符串,也可以是一个返回一个字符串的函数。
option
option是一个对象。这个选项也没用到过,但是有可能用到,先列出来。
- options.cwd(默认:process.cwd()) 当输出路径为相对路径的时候才有效
- options.mode(默认:0777) 新建目录的权限
gulp.task(name[, deps, fn])
gulp.task是用来定义任务的方法
1 2 3 4 5 6 7 8 9 10
| gulp.task('sass', function() { gulp.src('src/scss/master.scss') .pipe(sass()) .pipe(gulp.dest('dist/css')); }); gulp.task('styles',['sass'], function() { gulp.src('./dist/css/public.css') .pipe(minify()) .pipe(gulp.dest('dist/css')); });
|
第一个任务中没有依赖,直接function里面定义了任务行为,而第二个任务里面依赖了sass,只有sass执行完毕以后才会执行styles任务。
gulp.watch(glob [, opts], tasks)
gulp.watch是个用的相对较少,但是为了开发体验绝不可以或缺的方法。它提供了文件的动态监控。
需要掌握的不多,直接上个例子
1 2 3
| gulp.watch('js/**/*.js', function(event) { console.log('File ' + event.path + ' was ' + event.type + ', running tasks...'); });
|
这里只是把event进行了监视。实际上使用过程更多是队event.type状态进行对比,为true执行对应的任务。它可能的值是added, changed, deleted,一般情况我们判断是否为changed即可。
Gulp常用插件
Gulp倾向一个平台,在这个平台上有各种各样的组件可以选用,以达成任务构建目标。因此,常用的gulp插件的介绍还是必不可少的。
- css处理:
- 自动添加css前缀(gulp-autoprefixer)
- 压缩css(gulp-minify-css)
- sass编译(gulp-sass)
- js处理
- 合并js文件(gulp-concat)
- 生成js的map文件(gulp-sourcemaps)
- 压缩js代码(gulp-uglify)
- 图片处理
- 其他
- 服务和自动刷新(browser-sync)
- 删除文件和目录(del)
css任务
包括简单的编译,压缩,加私有前缀
1 2 3 4 5 6 7 8 9 10 11 12 13
| var gulp = require('gulp'), sass = require('gulp-ruby-sass'), autoprefixer = require('gulp-autoprefixer'), minifycss = require('gulp-minify-css');
gulp.task('styles', function() { return gulp.src('src/styles/main.scss') .pipe(sass({ style: 'expanded' })) .pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4')) .pipe(gulp.dest('dist/assets/css')) .pipe(minifycss()) .pipe(gulp.dest('dist/assets/css')) });
|
js任务
包括简单的语法检查,合并,压缩
1 2 3 4 5 6 7 8 9 10 11 12 13
| var gulp = require('gulp'), jshint = require('gulp-jshint'), uglify = require('gulp-uglify'), concat = require('gulp-concat'); gulp.task('scripts', function() { return gulp.src('src/scripts/**/*.js') .pipe(jshint('.jshintrc')) .pipe(jshint.reporter('default')) .pipe(concat('main.js')) .pipe(gulp.dest('dist/assets/js')) .pipe(uglify()) .pipe(gulp.dest('dist/assets/js')) });
|
图片压缩
1 2 3 4 5 6 7
| var gulp = require('gulp'), imagemin = require('gulp-imagemin'); gulp.task('images', function() { return gulp.src('src/images/**/*') .pipe(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true })) .pipe(gulp.dest('dist/assets/img')) });
|
监视任务
1 2 3 4 5
| gulp.task('watch', function() { gulp.watch('src/styles/**/*.scss', ['styles']); gulp.watch('src/scripts/**/*.js', ['scripts']); gulp.watch('src/images/**/*', ['images']); });
|
这里介绍到这样差不多可以了。
Gulp工作流实战。
在之前讲SystemJS时候,说到过整体的工作流。这里说一下自己如何处理这一块的东西。
但是在此之前先把之前的设计说明copy一下:
文件目录基础设计
这里简单说下文件目录基础设计:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| ├── asset │ ├── css │ ├── fonts │ ├── images │ └── js ├── config.js ├── html │ ├── dist │ │ └── login │ │ ├── login.html │ │ ├── logout.html │ │ └── resetPwd.html │ └── src │ └── login │ ├── login.html │ ├── logout.html │ └── resetPwd.html └── modules ├── business │ └── login │ ├── login.js │ ├── logout.js │ └── resetPwd.js ├── github ├── npm └── privateregistr
|
根据约定重于配置原则,这里说下设计和约定:
- 开发阶段使用html下src目录放置html文件,modules下business放置业务js和css代码,privateregistr这个随意命名的,用来放置公用模块,使用submodule维护。
- 发布阶段使用gulp对business下js和css进行打包,统一发布到asset/js和asset/css
- 每个html/dist下的文件夹为一个大栏目,里面每个html对应一个业务页面,每个页面的js名称结构要同modules/business下结构对应,举例来说:
- html/dist/login/login.html这个文件的对应的js是modules/business/login/login.js
- 每个页面只接受一个js作为业务入口,意思是如果上文login.js分解为step1.js&step2.js&step3.js是可以的,但是最终将只引入login.js一个,其他将require方式引用进去,不提供单独的script标签给它们。
- modules文件夹是jspm packages folder,里面的github和npm分别是github和npm下载的包,privateregistr则是使用jspm-git构建的私有包目标源。config.js是system.js的配置文件。
任务流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| var gulp = require('gulp'); var $ = require('gulp-load-plugins')(); var filewalker = require('filewalker'); var jsonfile = require('jsonfile'); var path = require('path'); var modulesPath = { moduleSrc : 'asset/src/js/', moduleDist: 'asset/dist/js' }; gulp.task('jspm', function(){ return gulp.src(modulesPath.moduleSrc+'**/*.js') .pipe($.jspm({ minify: true,sourceMaps: false,arithmetic: '- jquery - react - materialize'})) .pipe(gulp.dest(modulesPath.moduleDist)); });
gulp.task("inject",function(){ jsonfile.readFile("conf/injectJSON.json",function(err,json){ inject(json.file); }) });
gulp.task("json",jsonFac);
function inject(fileList){ var opts = { algorithm: 'sha1', hashLength: 8, template: '<%= name %><%= ext %>?v=<%= hash %>' }; for(var a=0,len=fileList.length;a<len;a++){ var assets = $.useref.assets({searchPath: ['asset/**/*','.']}); gulp.src(fileList[a].html) .pipe($.if(!!fileList[a].sources.js,$.inject(gulp.src(fileList[a].sources.js, {read: true}).pipe($.hash(opts)), {relative: true,removeTags:true}))) .pipe(assets) .pipe($.rev()) .pipe(assets.restore()) .pipe($.useref()) .pipe($.revReplace()) .pipe($.if('*.js', $.uglify())) .pipe(gulp.dest(fileList[a].dist)); } }
function jsonFac(){ var jsonData = { file:[] } filewalker('./html/src') .on('file', function(p, s) { if(/\.html$/.test(p)){ var jsPath; jsPath = 'asset/dist/js/'+p.replace(/\.html$/,'.bundle.js') jsonData.file.push({ html:'html/src/'+p, dist: path.dirname('html/dist/'+p), sources:{ js:jsPath } }) } }) .on('error', function(err) { console.error(err); }) .on('done', function() { var file = 'conf/injectJSON.json' jsonfile.writeFile(file, jsonData, function (err) { console.error(err) }); console.log('%d dirs, %d files, %d bytes', this.dirs, this.files, this.bytes); }) .walk(); }
|
使用时候分成3步:
- gulp json生成配置文件
- gulp jspm生成打包好的bundle文件
- gulp inject负责将资源插入大html然后进行压缩
实际上第三步骤里面包含的除了inject任务,还有css打包、压缩,私有前缀处理和强缓存处理。
当然,这并不是非常完善,但是处理SystemJS的应用确实是非常够用。
在这个流程中我们用到了npm的包,并非为gulp设计,但是配合npm的包,却在gulp当前没有合用插件情况下处理好了业务需求。这是一个非常有参考价值的实践。