首页 > 代码库 > grunt之dev-pro环境切换
grunt之dev-pro环境切换
在项目开发过程中和发布阶段需要在开发环境(dev)和生产环境(pro)之间切换,静态文件引用的切换等等。
使用grunt要如何解决上述问题,这里提供一个案列供参考。
用到的grunt插件:
文件合并:grunt-contrib-concat
javascript压缩:grunt-contrib-uglify
css 压缩:grunt-css
临时文件清理:grunt-contrib-clean
javascript代码检测:grunt-contrib-jshint
文件替换插件:grunt-string-replace
根据内容是否变化生成有哈希值文件名插件:grunt-rev
插件的具体用法可以到npm官网查看:https://www.npmjs.org/
在dev与pro之间切换的时候我们需要把页面上引用的未压缩合并的静态文件(A)和压缩合并后的文件(B)进行对应的切换操作,并且当文件内容改变后需要重新生成新的压缩合并文件用来处理cdn缓存问题。
考虑到随时可以进行环境切换所以项目中静态文件保留两份A和B,
在view页面上引入静态文件的地方加上标记用来方便查找切换,比如:
<!-- grunt-import-css bootstripCss --><link href="/Css/dest/603d3616.bootstrip.min.css" rel="stylesheet" /><!--/grunt-import --><!-- grunt-import-js mainJs --><script src="/Js/dest/3e083a76.main.min.js"></script><!--/grunt-import -->
标记自己配置的,只要方便查找就行。
在切换的时候遍历对应的文件夹里面所有的文件,查找文件里面出现匹配的标记,然后替换。(我这里用的是grunt-string-replace插件 ,也有类似其他的插件)
从dev切换到pro的时候需要检测压缩和的文件内容是否变化,变化了就生成对应的新的文件。
Gruntfile.js文件:
1 module.exports = function(grunt) { 2 var fs = require(‘fs‘); 3 4 // 配置 5 var isDev = false; //is develop 6 7 var cssLink = ‘<link href="http://www.mamicode.com/importUrl" rel="stylesheet" />‘, 8 jsLink = ‘<script src="http://www.mamicode.com/importUrl"><\/script>‘; 9 //视图文件路径 10 var viewPath = ‘Views‘; 11 //dev、pro环境对应静态文件关联配置 12 var staticConfig = { 13 ‘bootstripCss‘:{ 14 dev:[ 15 ‘Css/bootstrap.css‘, 16 ‘Css/font-awesome.min.css‘ 17 ], 18 pro:‘Css/dest/bootstrip.min.css‘ 19 }, 20 ‘IEhtml5Js‘:{ 21 dev:[ 22 ‘Js/html5shiv.js‘, 23 ‘Js/respond.min.js‘ 24 ], 25 pro:‘Js/dest/IEhtml5Js.min.js‘ 26 }, 27 ‘mainJs‘:{ 28 dev:[‘Js/Common.js‘,Js/main.js‘], 29 pro:‘/Js/dest/main.min.js‘ 30 } 31 }; 32 33 //concatConfig合并配置 uglifyJsConfig js压缩配置 cssminConfig css压缩配置 34 var concatConfig = {}, uglifyJsConfig = {}, cssminConfig = {}; 35 var fileType = ‘js‘; 36 var proFiles = []; //所有生产环境文件 37 for(var i in staticConfig){ 38 if(/css$/i.test(i)){ 39 fileType = ‘css‘; 40 }else if(/js$/i.test(i)){ 41 fileType = ‘js‘; 42 } 43 proFiles.push(staticConfig[i][‘pro‘]); 44 //配置合并的文件 45 concatConfig[i] = { 46 ‘files‘ : {} //{a:[b,c]} 目标文件,源文件 47 }; 48 if(staticConfig[i][‘options‘]){ 49 //合并配置项 50 concatConfig[i][‘options‘] = staticConfig[i][‘options‘]; 51 } 52 //合并的文件临时存放目录tmp 53 concatConfig[i][‘files‘][‘tmp/concat/‘+i+‘.‘+fileType] = staticConfig[i][‘dev‘].concat([]); 54 //js 压缩 55 if(fileType == ‘js‘){ 56 uglifyJsConfig[i] = { 57 ‘files‘ : {} //{a:[b,c]} 目标文件,源文件 58 }; 59 uglifyJsConfig[i][‘files‘][staticConfig[i][‘pro‘]] = [‘tmp/concat/‘+i+‘.‘+fileType]; 60 if(staticConfig[i][‘options‘]){ 61 //压缩配置项 62 uglifyJsConfig[i][‘options‘] = staticConfig[i][‘options‘]; 63 }else{ 64 uglifyJsConfig[i][‘options‘] = { 65 banner : ‘/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n‘ 66 } 67 } 68 }else if(fileType == ‘css‘){ 69 //css 压缩 70 cssminConfig[i] = { 71 ‘files‘ : {} //{a:[b,c]} 目标文件,源文件 72 }; 73 cssminConfig[i][‘files‘][staticConfig[i][‘pro‘]] = [‘tmp/concat/‘+i+‘.‘+fileType]; 74 if(staticConfig[i][‘options‘]){ 75 //压缩配置项 76 cssminConfig[i][‘options‘] = staticConfig[i][‘options‘]; 77 } 78 } 79 } 80 //获取对应路径里的文件 81 function getFileInfoFn(path){ 82 var fileInfo = [], 83 files = fs.readdirSync(path); 84 files.forEach(function(item) { 85 var tmpPath = path + ‘/‘ + item; 86 var stat = fs.lstatSync(tmpPath); 87 if (!stat.isDirectory()){ 88 fileInfo.push({‘file‘:tmpPath,‘cTime‘:fs.statSync(tmpPath).ctime}) 89 } else { 90 fileInfo = fileInfo.concat(getFileInfoFn(tmpPath)); 91 } 92 }); 93 return fileInfo; 94 } 95 96 //视图文件 97 var viewFiles = getFileInfoFn(viewPath); 98 //replaceConfig 在切换dev、pro环境时需要替换文件路径的视图文件配置 99 //gruntImportReg 替换的正则100 var gruntImportReg = /<!--\s*grunt-import-\w+\s+\w+\s*-->[\s\S]*?<!--\s*\/grunt-import\s*-->/ig;101 var gruntImportItemReg = /(<!--\s*grunt-import-(\w+)\s+(\w+)\s*-->)([\s\S]*?)(<!--\s*\/grunt-import\s*-->)/i;102 var replaceConfig = {103 ‘dist‘:{104 options: {105 replacements: [106 {107 pattern: gruntImportReg,108 replacement: function(matchStr){109 //搜索合并压缩的最新文件110 var fileInfo = getFileInfoFn(‘/Js/dest‘).concat(getFileInfoFn(‘Css/dest‘));111 fileInfo = fileInfo.sort(function(a, b){112 return a[‘cTime‘] - b[‘cTime‘];113 })114 for(var i in staticConfig){115 var proFile = staticConfig[i][‘pro‘].split(‘/‘);116 proFile = proFile[proFile.length -1].replace(/\./g,‘\\.‘);117 fileInfo.forEach(function(v, k){118 if(new RegExp("\\."+proFile).test(v[‘file‘])){119 staticConfig[i][‘pro‘] = v[‘file‘];120 return false;121 }122 })123 }124 125 gruntImportItemReg.lastIndex = 0;126 var matchItem = matchStr.match(gruntImportItemReg);127 var files = [], importLink = ‘‘,128 ret = matchItem[1]+‘\n‘;129 if(isDev){130 files = staticConfig[matchItem[3]][‘dev‘];131 }else{132 files = [staticConfig[matchItem[3]][‘pro‘]];133 }134 if(matchItem[2] == ‘js‘){135 importLink = jsLink;136 }else if(matchItem[2] == ‘css‘){137 importLink = cssLink;138 }139 files.forEach(function(v, k){140 ret += importLink.replace(‘importUrl‘, v);141 ret += ‘\n‘;142 });143 ret += matchItem[5];144 return ret;145 }146 }147 ]148 },149 files:{}150 }151 };152 viewFiles.forEach(function(v, k){153 replaceConfig[‘dist‘][‘files‘][v[‘file‘]] = v[‘file‘];154 });155 //grunt 配置156 grunt.initConfig({157 ‘pkg‘ : grunt.file.readJSON(‘package.json‘),158 ‘concat‘ : concatConfig, //合并任务159 ‘uglify‘ : uglifyJsConfig, //uglify js 压缩,160 ‘cssmin‘: cssminConfig, //css 压缩,161 ‘clean‘: {162 test: [‘tmp‘] //创建的临时文件163 },164 ‘jshint‘: {165 js: [‘Js/*.js‘, ‘Js/**/*.js‘,‘Js/**/**/*.js‘]166 },167 ‘string-replace‘:replaceConfig, //替换路径168 ‘watch‘: {169 scripts: {170 files: [‘Js/*.js‘, ‘Js/**/*.js‘,‘Js/**/**/*.js‘],171 tasks: [‘jshint‘]172 }173 },174 ‘rev‘: {175 files: {176 src: proFiles177 }178 }179 });180 181 // loadNpmTasks182 grunt.loadNpmTasks(‘grunt-contrib-concat‘);183 184 grunt.loadNpmTasks(‘grunt-contrib-uglify‘);185 186 grunt.loadNpmTasks(‘grunt-css‘);187 //clear188 grunt.loadNpmTasks(‘grunt-contrib-clean‘);189 190 grunt.loadNpmTasks(‘grunt-contrib-jshint‘);191 192 //dev、production193 194 grunt.loadNpmTasks(‘grunt-string-replace‘);195 196 grunt.loadNpmTasks(‘grunt-contrib-watch‘)197 198 grunt.loadNpmTasks(‘grunt-rev‘);199 200 //注册任务:201 202 // 默认任务203 grunt.registerTask(‘default‘, [‘concat‘, ‘uglify‘,‘cssmin‘,‘clean‘]);204 205 grunt.registerTask(‘jsHint‘, [‘jshint‘]);206 207 grunt.registerTask(‘watch‘, [‘watch‘]);208 209 //根据文件内容生产文件210 grunt.registerTask(‘setCacheFile‘,[‘rev‘]);211 //切换dev pro 环境212 grunt.registerTask(‘transfer‘,[‘string-replace‘]);213 214 grunt.registerTask(‘quick‘, [‘default‘, ‘setCacheFile‘, ‘transfer‘]);215 216 };
package.json:
1 { 2 "name": "test2", 3 "version": "0.1.0", 4 "author": "bossliu", 5 "homepage": "###", 6 "devDependencies": { 7 "grunt": "~0.4.0", 8 "grunt-contrib-clean":"~0.4.0rc5", 9 "grunt-contrib-jshint": "~0.1.1rc5",10 "grunt-contrib-uglify": "~0.1.2",11 "grunt-contrib-concat": "~0.1.1",12 "grunt-string-replace":"~0.2.7",13 "grunt-contrib-watch":"~0.6.1",14 "grunt-rev":"~0.1.0",15 "grunt-css": ">0.0.0"16 }17 }
index.html:
<!DOCTYPE html><html><head><title>grunt test</title><!-- grunt-import-css bootstripCss --><link href="Css/dest/603d3616.bootstrip.min.css" rel="stylesheet" /><!--/grunt-import --></head><body> grunt test<!-- grunt-import-js IEhtml5Js --><script src="Js/dest/56b83730.IEhtml5Js.min.js"></script><!--/grunt-import --><!-- grunt-import-js mainJs --><script src="Js/dest/3e083a76.main.min.js"></script><!--/grunt-import --></body></html>
参考文档:
http://www.infoq.com/cn/news/2014/03/env-spec-build-tool-compare/
http://www.infoq.com/cn/articles/front-end-engineering-and-performance-optimization-part1
grunt之dev-pro环境切换