离开qunar有一个多月了,在离开的时候就决定再也不用fekit。作出这个决定并非由于fekit很差,偏偏相反,fekit帮咱们作了不少事情,还屏蔽了许多细节,让开发人员可以专一于开发过程。不过随着fekit的升级,也出现了一些问题,同时fekit和公司业务及发布流程有必定耦合,因此以为采用开源的构建方案。 php
在使用gulp的过程当中,基本也是依据使用fekit的思路来逐步完善构建过程的,因此仍是要感谢fekit。如今进入正题,
看看利用gulp如何实现本地服务、mock数据、css预处理、js模块化、打包压缩、版本号替换等功能的。css
依赖模块:gulp-webserverhtml
使用:前端
gulp.task('webserver', function() { gulp.src('./app') .pipe(webserver({ livereload: true, directoryListing: { enable:true, path: 'app' }, host: '172.16.100.27', port: 8000 })); });
注意:python
app是你的项目目录,好比个人目录结果以下jquery
-app |--- src |--- images |--- mock |--- prd |--- index.html -gulpfile.js
开启directoryListing的enbale,访问根目录(172.16.100.27:8000)时才能显示目录或文件列表。webpack
host能够是ip也能够是域名,不过在虚拟机里面调试时,用域名访问彷佛有点问题,没有深究;若是要在手机上调试,一样建议用ip。git
依赖模块:gulp-webservergithub
使用:请参考以前的博文 gulp构建之mock data(模拟数据、转发请求)web
依赖模块:gulp-sass gulp-minify-css gulp-autoprefixer
使用:我这里使用的是sass
var sass = require('gulp-sass'), minifyCSS = require('gulp-minify-css'), autoprefix = require('gulp-autoprefixer'); var cssFiles = [ 'app/src/styles/page/index.scss', 'app/src/styles/page/touch.scss' ]; /* 编译压缩sass文件 */ gulp.task('sass', function() { gulp.src(cssFiles) .pipe(sass().on('error', sass.logError)) .pipe(autoprefix()) .pipe(minifyCSS()) .pipe(gulp.dest(paths.build.styles)); });
注意:
使用sass模块时,要加上.on('error', sass.logError))
,这样sass编译出错时会打log,而不是终止运行。
autoprefix,这是一个好东西,也是我不用fekit的一个缘由(它还没集成autoprefix)
依赖模块:gulp-webpack gulp-uglify vinyl-named imports-loader
说明:fekit采用的CommonJs的模块化方式,此外还有requireJS这种AMD的模块化方式。而webpack则是理想中的打包工具,它同时支持CommonJs和AMD,并拥有大量的加载器,包括上面说都的sass编译、前端模板编译等加载器,并且还有webpack-dev-server,已经强大到几乎不须要gulp了。我对webpack还不太熟,而且须要用到gulp的一些其余功能,因此暂时考虑把webpack做为gulp的一个模块来使用。
使用:
var named = require('vinyl-named'); var webpack = require('gulp-webpack'); var jsFiles = [ 'app/src/scripts/page/index.js', 'app/src/scripts/page/touch.js' ]; // webpack打包压缩js gulp.task('packjs', function() { return gulp.src(jsFiles) .pipe(named()) .pipe(webpack({ output: { filename: '[name].js' }, module: { loaders: [{ test: /\.html$/, loader: 'mustache' }, { test: /\.js$/, loader: "imports?define=>false" }] }, resolve: { alias: { jquery: 'app/src/scripts/lib/jquery-1.11.3.min.js' } }, devtool: "#eval-source-map" })) .pipe(uglify().on('error', function(e) { console.log('\x07',e.lineNumber, e.message); return this.end()} )) .pipe(gulp.dest(paths.build.scripts)); });
注意:
对于有多个入口文件(entry point)的状况,须要使用vinyl-named这个模块,这样就能实现如下打包需求
src/scripts/index.js -> prd/scripts/index.js src/scripts/touch.js -> prd/scripts/touch.js
在output里面能够设置打包后的文件名,如 "[name].min.js"
我在项目中使用的是commonjs的模块化方式,但大多数插件(如jquery或zepto插件)都是支持两种模块化方式,而且是先判断 define 再判断 module.export,因此咱们须要“屏蔽”amd的模块加载方式。
首先安装`imports-loader`模块,而后作以下配置:
module: { loaders: [{ test: /\.js$/, loader: "imports?define=>false" }] }
这样咱们就能放心地使用各类插件了。
个人项目中使用了HoganJs做为前端模块,在webpack中只要找到相应的加载器就行。
首先安装[mustache-loader](https://github.com/deepsweet/mustache-loader)模块,而后作以下配置:
module: { loaders: [{ test: /\.html$/, loader: 'mustache' }] }
经过 2. 和 3. 两个例子,你们能够看出test就是要处理的文件类型,loader就是处理文件的加载器。
调试仍然是个比较大的问题,虽然webpack提供了各类调试模式(在devtool
中配置,实现sourcemap的调试),但在实际使用时,常常会遇到跳过断点的问题,不知道是否是gulp-webserver和webpack不适配的缘由。因此在实际开发中,我会先把uglify给注释掉,这样至少能在一个未混淆压缩的文件里调试。若是你们有好的方法,麻烦告知,谢谢~
依赖模块:gulp-rev gulp-rev-collector
说明:在实际生产环境中,咱们页面引用的css和js文件的文件名都是带版本号的,这样方便回滚和防止缓存。一般咱们使用文件的md5编码做为版本号。
使用:
var rev = require('gulp-rev'), revCollector = require('gulp-rev-collector'); var cssDistFiles = [ 'app/prd/styles/index.css' ]; var jsDistFiles = [ 'app/prd/scripts/index.js' ]; // prd文件加md5后缀,并生成替换map gulp.task('ver', function() { gulp.src(cssDistFiles) .pipe(rev()) .pipe(gulp.dest('app/prd/styles')) // 生成 name-md5.css .pipe(rev.manifest()) .pipe(gulp.dest('app/ver/styles')); // 生成 rev-manifest.json(映射) gulp.src(jsDistFiles) .pipe(rev()) .pipe(gulp.dest('app/prd/scripts')) .pipe(rev.manifest()) .pipe(gulp.dest('app/ver/scripts')); }); // html文件添加md5引用 gulp.task('html', function() { gulp.src(['app/ver/**/*.json', 'app/*.html']) .pipe(revCollector()) .pipe(gulp.dest('app/')); });
结果以下:
index.html <script src="prd/scripts/index.js"></script> => <script src="prd/scripts/index-0884a4f91b.js"></script>
注意:
我把生产md5和替换html中的版本号拆分为了两步,以前是放在一块儿,结果会出现用上一次版本号替换html中文件名的问题。
依赖模块:gulp-inline-source
说明:一些touch上的活动页,样式和脚本都很少,与其增长额外的请求数,不如把样式和脚本都之内联的方式嵌到html文件中。
使用:
gulpfile.js // 把css、js以inline的形式插入html gulp.task('inlinesource', function () { return gulp.src('app/index.html') .pipe(inlinesource()) .pipe(gulp.dest('dist')); }); index.html <link rel="stylesheet" href="../prd/styles/index.css " inline> <script src="../prd/scripts/index.js" inline></script>
这一个月来,我用到的基本就这么多了,其实回头看看,本身东拼西凑也算是造了一个本身的构建小工具。同时,我也更深刻地理解了fekit的设计思路和一些原理。
不过,相似FIS的smarty模板、fekit的velocity mock等功能,我还没发现怎么在gulp中来实现。简单的说,就是能在前端环境开发jsp、velocity或者php(我目前的需求是php),数据采用mock方式,不依赖于后端,从而把view的控制权彻底拿到前端,实现先后端的分离(非ajax方式)。若是你们有任何建议,麻烦指教!
更新于08.11
依赖模块: gulp-swig
说明:swig 是一个类django、twig模板的前端模版,说是相似,基本语法其实同样,这样前端开发用swig,后端用对应的模板引擎(好比python的django、php用twig等),这样一套模版文件在先后端都能解析,从而实现先后端分离。
因为swig和twig在一些语法上存在差别,咱们须要扩展swig:
swig.setDefaults({ cache: false, loader: swig.loaders.fs(path.join(appbase)), locals: { environment: "local", // 全局变量,表示本地环境,用于区分swig和twig不同的地方 range: function (start, end) { return (new Array(end-start+1)).join().split(',').map(function (n, idx) { return idx + start; }); } } }); swig.setFilter('length', function (input) { return input.length; }); swig.setFilter('slice', function(input, begin, len){ return input.slice(begin, len); }); swig.setFilter('json_encode', function(input){ return JSON.stringify(input); }); swig.setFilter('replace', function(input, obj) { var output = input; for (var key in obj) { output = output.replace(key, obj[key]); } return output; });
另外我在locals里面设置了environment这个字段,这样在某些地方能够经过environment判断是不是本地环境,从而解决swig和twig不兼容的问题:
{% if environment == 'local' %} {% set tpl_path = './components/ctn_publish/' + type + '/index.tpl' %} {% else %} {% set tpl_path = './components/ctn_publish/' ~ type ~ '/index.tpl' %} {% endif %}