用gulp做一个略完整的前端打包工作~
来源:互联网 发布:linux 安装tar命令 编辑:程序博客网 时间:2024/05/01 23:29
分模块,每个模块都有独立的页面和静态文件,并将所有静态文件打在一个文件夹下,
一些常用变量可以进行替换,并可进行简单的页面动态生成,
生产环境打包与线上环境打包分开进行,
静态文件进行压缩合并,加md5以及cdn,
wap的静态文件与web端分离,wap的页面文件没有的继承web端的页面文件,有的要用它本身。
打包由grunt换成gulp。
整体的路径要像这样:
好了,那我们开始,首先当然是要安装gulp
npm install gulp --save-dev
然后你需要在你的环境里加一个gulpfile.js的文件。
然后我们为了高大上一点,给弄个简易的手册出来:
直接console.log输出就好了···
// 说明gulp.task('help',function () { console.log('gulp build文件打包'); console.log('gulp watch文件监控打包'); console.log('gulp helpgulp参数说明'); console.log('gulp server测试server'); console.log('gulp -p生产环境(默认生产环境)'); console.log('gulp -d开发环境'); console.log('gulp -m <module>部分模块打包(默认全部打包)');});/* 默认 */gulp.task('default',function () { gulp.start('help');});
然后在你的控制台输入 gulp help或者 gulp build 就可以看到效果啦
然后我们要建立我们的build任务啦~
首先我们需要接收参数判断是开发打包还是生产打包。这里需要一个插件-- yargs,以及我们以后需要用到的工具类的插件:lodash(用于操作数组,当然它不仅仅是有这样的用处)和 path(用于写路径),用npm像上面那样加到你的环境中,
并且在你的gulpfile文件中声明它们:
/* 载入工具 */var argv = require('yargs').argv, _ = require('lodash'), path = require('path');
记得之后载入的插件都需要这样声明。
我们新建一个叫build的task
//创建任务
gulp.task('build', function() { });
在里面获取一下控制台输入的参数:
var evr = argv.p || !argv.d; //生产环境为true,开发环境为false,默认为true
var mod = argv.m || 'all';//模块明,默认为全部
这样你在控制台输入 gulp build -p 或者 gulp build -p -m t1这样就可以在里面获得参数啦。
然后我们需要进行一些打包的配置,先在写一个叫FileConfig的js文件,在里面写我们需要的文件配置信息:
"use strict";var path = require('path');/* 环境信息 */var source = 'source', develop = 'develop', production = 'production'; /* src路径 */var src = { tpl: 'tpl/**', css: 'styles/**/*.less', js: 'lib/**/*.js', html: '/**.html', img: 'images/**'}/* 模块信息 */var modules = { "t1": { src: 't1', dest: 't1', name: 't1', css_files: 'styles/app.less' }, "t2": { src:'t2', dest: 't2', name: 't2' }, "index": { src:'index', dest: 'index', name: 'index' }, "common": { src:'common', dest: 'common', name: 'common' }};
然后在下面写个方法:
var FileConfig = function () {
};
为了省事儿,直接在exports的时候就new了它···
module.exports = new FileConfig();
之后我们的一些配置信息全部从FileConfig这个方法里取,比如要模块信息的话:
FileConfig.prototype.modules = function () {
return modules;
};
之后的具体实现就不再啰嗦啦。
然后我们回到gulpfile.js文件下,声明他,之后会用到。
然后我们就要对html,css,js,img进行处理啦,这里会用到很多插件,一次性的列举出来,用法我在后面的注释中写了:
// 载入外挂var gulp = require('gulp'), browserify = require('browserify'),//这里用不上,管理js依赖的 source = require('vinyl-source-stream'),//同样这里用不上,和上面那个一起的 uglify = require('gulp-uglify'),//混淆js clean = require('gulp-clean'),//清理文件 notify = require('gulp-notify'),//加控制台文字描述用的 buffer = require('vinyl-buffer'), less = require('gulp-less'),//转换less用的 autoprefixer = require('gulp-autoprefixer'),//增加私有变量前缀 minifycss = require('gulp-minify-css'),//压缩 concat = require('gulp-concat'),//合并 fileinclude = require('gulp-file-include'),// include 文件用 template = require('gulp-template'),//替换变量以及动态html用 rename = require('gulp-rename'),//重命名 webserver = require('gulp-webserver'),//一个简单的server,用python的SimpleHttpServer会锁文件夹 imagemin = require('gulp-imagemin'),//图片压缩 gulpif = require('gulp-if'),//if判断,用来区别生产环境还是开发环境的 rev = require('gulp-rev'),//加MD5后缀 revReplace = require('gulp-rev-replace'),//替换引用的加了md5后缀的文件名,修改过,用来加cdn前缀 addsrc = require('gulp-add-src'),//pipeline中途添加文件夹,这里没有用到 del = require('del'),//也是个删除··· vinylPaths = require('vinyl-paths'),//操作pipe中文件路径的,加md5的时候用到了 runSequence = require('run-sequence');//控制task顺序
好啦,为了省事儿,写个对象字面量的方法集:
/* 打包方法 */var teemoGulp = { /* html打包 */ buildHtml : function () {}, /* css 打包 */ buildCss : function () {}, /* js打包 */ buildJs : function () {}, /* img打包 */ buildImg: function () {}, /* md5打包 */ buildMd5: function () {}}
然后我们先说简单的,js打包,只需要初始路径和打包后路径就好啦:
/* js打包 */ buildJs : function () { var src = arguments[0],dest = arguments[1], flag = arguments[2] return gulp.src(src) .pipe(gulp.dest(dest)) .pipe(uglify()) .pipe(rename({ suffix: '.min' })) .pipe(gulp.dest(dest)); }
其中flag是之前的这个var evr = argv.p || !argv.d; //生产环境为true,开发环境为false,默认为true,这个比较简单,先把原始文件拷贝一份,在混淆压缩加.min后缀存一份。
img打包也很简单
/* img打包 */ buildImg: function () { var src = arguments[0],dest = arguments[1], flag = arguments[2] return gulp.src(src) .pipe(gulpif(flag, imagemin())) .pipe(gulp.dest(dest)); },
这里注意一下用到了 gulpif 根据环境来判定是否需要做 图片的压缩操作,因为这项操作比较费时间,开发环境下就不进行了。
css打包和html打包需要用到一些存好的变量,比如,如果我们需要每个页面的keywords是一样的:
<meta name="keywords" content="<%- keywords %>">
我们就需要给这些变量一个配置的地方,这个和fileconfig是一样的,就不再说了。然后替换这些变量我们要用到 gulp-template,他继承了lodash的template的用法,也就是说,他可以识别js代码以及lodash的一些用法。
而当我们想增加一些文件时,就要用到gulp-file-include,或者你直接改lodash,让他支持html代码也可以,不过我不赞成这样做。
他的用法类似这样:
@@include('t1/tpl/menu.html')
整体的html打包代码是这样:
/* html打包 */ buildHtml : function () { var src = arguments[0],dest = arguments[1], flag = arguments[2], options = arguments[3]; return gulp.src(src) .pipe(fileinclude({ basepath:basePath.source })) .pipe(template(options,{ //interpolate: /\{-([\s\S]+?)-\}/g })) .pipe(gulp.dest(dest)) },
template默认的包裹变量的标示是 <%-%>当然你可以用 interpolate去配置这些。
好啦,方法我们写好了,下面是引用他们,我们需要在写一个方法,
var build = funciton () {}
然后获取参数信息,并进行必要的配置,让他们可以按照我们想要的方式输出。
var evr = options.evr,mod = options.mod; if(!modules[mod]) mod = 'all'; /* 路径初始化 */ fileConfig.init(evr); gulp.task('clean', function() { if(mod === 'all') { var clean_path = path.join( evr&& basePath.production || basePath.develop, '/'); return gulp.src([clean_path], {read:false}) .pipe(clean()); } }); /* 获取要build的模块 */ var parts = []; if(mod === 'all') { parts = modules; }else { parts = _.pick(modules,mod); }
之后我们循环遍历modules,建立相应的task
/* 分别对模块进行建立 */ var list = []; for( var key in parts){ (function (key) {//闭个包 var options = fileConfig.getModule(parts[key]); /* js建立 */ gulp.task(key+'_js',function () { return teemoGulp.buildJs(options.js.src,options.js.dest,evr); }); /* css建立 */ gulp.task(key+'_css',function () { return teemoGulp.buildCss(options.css.src,options.css.dest,evr, teemoConfig, options.css.filename); }); /* html建立 */ gulp.task(key+'_html',function () { return teemoGulp.buildHtml(options.html.src,options.html.dest,evr, teemoConfig); }); /* img建立 */ gulp.task(key+'_img',function () { return teemoGulp.buildImg(options.img.src,options.img.dest,evr); }); /* 模块建立 */ gulp.task(key+'_module',function(cb) { runSequence( [key+'_html',key+'_css',key+'_js',key+'_img'], cb ); } ); })(key); list.push(key+'_module'); }
这里用到了runSequence,它制定了task的执行顺序,因为如果不这样做,gulp默认是尽可能的并发执行,就有可能出现 clean没执行完,后续就开始执行的结果,这里注意吧task的回调放在runSequence的最后,保证内部的task执行完,再执行完本身。
当然如果比较简单的,你可以用task的第二个参数来控制task的执行顺序,这里不啰嗦啦。
然后把list返还给你最初的 build
var list = Build({ evr: evr, mod: mod }); var md5 = evr&& 'md5' || []; runSequence( 'clean',list, 'md5',cb );
然后顺序执行即可。
之后到了md5的阶段,md5打包用rev插件即可,但是他默认的md5码是10个,你可以去修改gulp-rev\node_modules\rev-hash路径下的index.js文件,或者干脆做个变量在你的filegulp里配置,单我觉得没必要,就偷懒这里改一下啦。我喜欢用16位
在之后需要把这些加了md5的文件名字换给你引用这些文件的地方,就用到了 rev-replace。
rev在改路径时候会生成一个映射表,你用rev.manifest()即可储存在相应的目录里,然后rev-replace用这个文件替换掉你目标文件的路径即可。
至于cdn,也可以用revReplace里的prefix属性去加前缀,不过他只支持字符串,如果我们是有多个cdn域名的话,返回给他的可能是一个方法,所以这里我们需要改一下rep-replace文件。
找到gulp-rev-replace下的index.js文件。
在第36行加一句
var prefix = typeof options.prefix === 'function' ? options['prefix'](path.basename(file.path)) : options.prefix;
用prefix替换掉一下用到options.prefix的地方。你需要在第60行左右做同样的事情:
if (options.manifest) { // Read manifest file for the list of renames. options.manifest.on('data', function (file) { var manifest = JSON.parse(file.contents.toString()); Object.keys(manifest).forEach(function (srcFile) { var prefix = typeof options.prefix === 'function' ? options['prefix'](path.basename(srcFile)) : options.prefix; //这句是加的 renames.push({ unreved: canonicalizeUri(srcFile), reved: prefix + canonicalizeUri(manifest[srcFile]), prefix: prefix //这句也是后加的 }); }); }); options.manifest.on('end', replaceContents); } else { replaceContents(); }
而且你会发现在之后替换的时候也用到了这个,所以你要他当局部变量传进renames里去。想上面写的那样,因为下面一句用到了他:
if (rename.prefix) { contents = contents.split('/' + rename.prefix).join(rename.prefix + '/'); //这句也改了 }
它这替换方式也挺丧良心的···
这样,我们就可以往里面传方法了~
网上找个算cdn域名的方法:
/*生成hash */ hashFnv32a : function(str, asString, seed) { /*jshint bitwise:false */ var i, l, hval = (seed === undefined) ? 0x811c9dc5 : seed; for (i = 0, l = str.length; i < l; i++) { hval ^= str.charCodeAt(i); hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); } if( asString ){ // Convert to 8 digit hex string return ("0000000" + (hval >>> 0).toString(16)).substr(-8); } return hval >>> 0; }
然后我们就可以进行md5加cdn的打包操作了:
/* md5打包 */ buildMd5: function () { var vp = vinylPaths(); return gulp.src(path.join(basePath.production, '/**/{images,lib,styles}/**/*.*')) .pipe(vp) .pipe(rev()) .pipe(gulp.dest(basePath.production)) .pipe(rev.manifest()) .pipe(gulp.dest(basePath.production)) .on('end',function () { var manifest = gulp.src(path.join(basePath.production, "/rev-manifest.json")); gulp.src(path.join(basePath.production, '/**/*.{css,html}')) .pipe(revReplace({ manifest: manifest, prefix: function (f) { //cdn域名 var cdns = ["s1","s2","s3","s4"]; var fileHashCode = teemoGulp.hashFnv32a(f); if(fileHashCode <0 ){ fileHashCode = 0; } var cdn = cdns[fileHashCode % cdns.length]; return 'http://'+cdn+'.tm.sogoucdn.com/w/20141209' } })) .pipe(gulp.dest(basePath.production)) .on('end',function () { del(vp.paths); del(path.join(basePath.production, "/rev-manifest.json")); }) }) },
这样,整个的打包就完成了啦,压缩混淆合并加md5加cdn就全都搞定了。
至于加wap的文件夹,就在做一遍之前的操作即可,记得html要在做一遍覆盖操作,也就是三遍。
需要注意的是你改过的插件需要自己存到特定的包管理的地方去,或者存到自己的github上也行,配好ssh就能下。
最后你需要一个 测试的server,这个很好配:
/* server */gulp.task('server', function() { var evr = argv.p || !argv.d; gulp.src(evr?basePath.production:basePath.develop) .pipe(webserver({ livereload: true, directoryListing: true, open: true }));});
不过如果你要测试cdn的话,就只能自己配nginx了,gulp-server上倒是能配代理,不过我没用过,应该也好用。
这样,所有的工作就完成了,这样做还是很方便的,比如,主管要求生产环境和开发环境的路径上要在家 一层路径,那么我们直接修改fileConfig里面的配置就可以:
/* 环境信息 */var source = 'source', develop = 'build/develop', production = 'build/production';
- 用gulp做一个略完整的前端打包工作~
- gulp-用gulp做一个略完整的前端打包工作
- Gulp教程,前端工作环境的搭建
- gulp教程 前端工作环境的搭建
- Gulp针对F1平台的前端资源打包说明
- angular js+gulp+jenkins打包(略git)
- webpack gulp 实现完整前端工程化
- Gulp教程之:Gulp能做什么,前端装逼为何要用它
- 做一个略调皮的个人主页--结构篇
- 做一个略调皮的个人主页--菜单篇
- 做一个略调皮的个人主页--相册与随笔篇
- 做一个略调皮的个人主页--游戏篇
- 做一个略调皮的个人博客--菜单篇
- 前端打包构建工具gulp入门实战
- nodejs-gulp 打包前端项目代码
- gulp——打造一个简单的前端自动化项目
- 一个完整的编译器前端实验
- 做一个完整的网站用什么技术最好?
- SonarQube代码规范-equals与Automic类
- Android随笔记
- 【LeetCode-387】First Unique Character in a String(C++)
- Android之自定义对话框AlertDialog.Builder+getLayoutInflater().inflate
- 【Servlet】HttpServletRequest和HttpServletResponse
- 用gulp做一个略完整的前端打包工作~
- 解决注销登录进入登录界面,避免返回到主界面
- Android RecyclerView Edittext issue 解决RecyclerView中editext获取焦点紊乱现象
- CAN电平--隐性与显性
- C++简单的TCP/IP通信
- Git概念 及相关操作上传、合并等
- android google map
- Android GridView自适应Item高度
- SQL Server分页存储过程