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="importUrl" rel="stylesheet" />‘,
  8         jsLink = ‘<script src="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: proFiles
177             }
178         }
179     });
180
181     // loadNpmTasks
182     grunt.loadNpmTasks(‘grunt-contrib-concat‘);
183
184     grunt.loadNpmTasks(‘grunt-contrib-uglify‘);
185
186     grunt.loadNpmTasks(‘grunt-css‘);
187     //clear
188     grunt.loadNpmTasks(‘grunt-contrib-clean‘);
189
190     grunt.loadNpmTasks(‘grunt-contrib-jshint‘);
191
192     //dev、production
193
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

时间: 2024-09-30 16:06:46

grunt之dev-pro环境切换的相关文章

spring通过profile实现开发和测试环境切换

以开发测试为例,介绍tomcat部署应用和maven部署应用下利用profile实现测试环境和开发环境切换 一.tomcat部署应用 1.数据源配置 dev.properties 路径:/src/main/resrouces jdbc.database=MYSQL jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://mysql:3306/develop?useUnicode=true&characterEncoding=utf-8 jd

spring boot--日志、开发和生产环境切换、自定义配置

Spring Boot日志常用配置: # 日志输出的地址:Spring Boot默认并没有进行文件输出,只在控制台中进行了打印 logging.file=/home/zhou # 日志级别 debug-> info -> warning -> error # 默认级别为 info # 如果设置了debug=true的时候,日志级别会自动降低为debug # ROOT代表默认全局设置 logging.level.ROOT=INFO # 可以设置指定包的输出级别,这样的话,指定的包,级别以下

开发环境切换

2014年10月24日 15:25:38 开发时经常遇到要线上线下环境切换 1) 火狐装插件: hostsadmin, chrome插件: hosts-manager 2) 直接编辑 hosts文件 + 强刷/重启浏览器 3) 让公司给配两台电脑,一台线下,一台线上 ;) 3) proxy切换, 最近发现一个很爽的方法,公司给开了代理,通过chrome的一个插件switchysharp,可以控制代理的打开和关闭, hosts配置好线下的硬解析,需要切换到线上的时候,点击chrome里的switc

spring 环境切换

软件开发过程一般有三个阶段:开发 > 测试 > 生产.每个阶段都对应不同的数据库环境配置,我们希望通过一种自动切换的方式来减少手动切换的工作量,这样做的目的也是为了能够减少手工带来的出错率. 具体配置步骤如下: 1.在resource目录下建立每种环境对应的文件夹,用来存放配置文件. development文件夹 : 存放 dev.properties production文件夹 : 存放 produce.properties test 文件夹: 存放 test.properties 2. s

如何实现Visual Studio中的区域语言环境切换

最开始学习C#这门语言的时候,英文能力不够好,安装的中文版本的Visual Studio,现在工作有段时间了,公司又是个外企,慢慢不习惯中文版本的了,于是产生了想切换语言的想法,网上搜索了下,下载了个语言包,然后在VS中设置下. 从某种角度上讲,VS作为编程软件,供世界上很多程序员使用,各个程序员都有自己习惯的语言,于是展现在他们面前的UI就有各个语言的版本,这也就是国际化. 具体切换步骤如下: 在“工具”菜单中选择“选项” ->展开“选项”列表,选择“环境”,单击“区域设置”,在“语言”下拉列

Grunt 安装与配置环境

当时学习 Grunt 的时候,真是很头疼.分了两个时间段,学习了两次才硬啃下来,之后才能用在项目中.主要原因我认为是学习资料和文档上面写的太高端了.这类的文档或者资料有个显著特点,上来先简单介绍一下这个玩意(Grunt 是一个 JavaScript 任务运行器),然后就是如何安装,直接给你配置文件的语法,如何使用插件,新手往往看完还不知所以然. 就像我第一次学习的时候,只是大体知道 Grunt 很火,大家都在用,但耐着心看文档和一些别人的学习总结,还是困惑,这到底是个什么东西?究竟干什么用?为什

Android app内语言环境切换

逻辑很简单: 1  app内所有activity继承自BaseActivity或BaseActivity派生出来的子类,BaseActivity中维护了一个静态的app Activity访问栈,在创建和销毁时会执行压栈和出栈操作,所以mLocalStack内维持的是app中正在运行的activity. 2  将app的语言环境存储在SharedPreferences中,避免app重启时修改状态不改变:在BaseActivity创建时取出语言环境字符串并初始化Activity语言环境(initLa

开发环境和发布环境切换以方便测试

需求:点击某个地方触发事件,可以自由的切换测试.预生产.生产三种环境. 原理:用NSUserDefault或者Singleton去维护环境变量集合. 宏定义配置 /***************单例模式宏**************/ #define MACRO_SHARED_INSTANCE_INTERFACE +(instancetype)sharedInstance; #define MACRO_SHARED_INSTANCE_IMPLEMENTATION(CLASS) \ +(insta

ASP.NET Core根据环境切换NLog配置

1.新建NLog配置文件,名称分别为nlog.config和nlog.debug.config <?xml version="1.0"?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" autoReload="true" inter