在项目开发过程中和发布阶段需要在开发环境(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文件:

 module.exports = function(grunt) {
var fs = require('fs'); // 配置
var isDev = false; //is develop var cssLink = '<link href="importUrl" rel="stylesheet" />',
jsLink = '<script src="importUrl"><\/script>';
//视图文件路径
var viewPath = 'Views';
//dev、pro环境对应静态文件关联配置
var staticConfig = {
'bootstripCss':{
dev:[
'Css/bootstrap.css',
'Css/font-awesome.min.css'
],
pro:'Css/dest/bootstrip.min.css'
},
'IEhtml5Js':{
dev:[
'Js/html5shiv.js',
'Js/respond.min.js'
],
pro:'Js/dest/IEhtml5Js.min.js'
},
'mainJs':{
dev:['Js/Common.js',Js/main.js'],
pro:'/Js/dest/main.min.js'
}
}; //concatConfig合并配置 uglifyJsConfig js压缩配置 cssminConfig css压缩配置
var concatConfig = {}, uglifyJsConfig = {}, cssminConfig = {};
var fileType = 'js';
var proFiles = []; //所有生产环境文件
for(var i in staticConfig){
if(/css$/i.test(i)){
fileType = 'css';
}else if(/js$/i.test(i)){
fileType = 'js';
}
proFiles.push(staticConfig[i]['pro']);
//配置合并的文件
concatConfig[i] = {
'files' : {} //{a:[b,c]} 目标文件,源文件
};
if(staticConfig[i]['options']){
//合并配置项
concatConfig[i]['options'] = staticConfig[i]['options'];
}
//合并的文件临时存放目录tmp
concatConfig[i]['files']['tmp/concat/'+i+'.'+fileType] = staticConfig[i]['dev'].concat([]);
//js 压缩
if(fileType == 'js'){
uglifyJsConfig[i] = {
'files' : {} //{a:[b,c]} 目标文件,源文件
};
uglifyJsConfig[i]['files'][staticConfig[i]['pro']] = ['tmp/concat/'+i+'.'+fileType];
if(staticConfig[i]['options']){
//压缩配置项
uglifyJsConfig[i]['options'] = staticConfig[i]['options'];
}else{
uglifyJsConfig[i]['options'] = {
banner : '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
}
}
}else if(fileType == 'css'){
//css 压缩
cssminConfig[i] = {
'files' : {} //{a:[b,c]} 目标文件,源文件
};
cssminConfig[i]['files'][staticConfig[i]['pro']] = ['tmp/concat/'+i+'.'+fileType];
if(staticConfig[i]['options']){
//压缩配置项
cssminConfig[i]['options'] = staticConfig[i]['options'];
}
}
}
//获取对应路径里的文件
function getFileInfoFn(path){
var fileInfo = [],
files = fs.readdirSync(path);
files.forEach(function(item) {
var tmpPath = path + '/' + item;
var stat = fs.lstatSync(tmpPath);
if (!stat.isDirectory()){
fileInfo.push({'file':tmpPath,'cTime':fs.statSync(tmpPath).ctime})
} else {
fileInfo = fileInfo.concat(getFileInfoFn(tmpPath));
}
});
return fileInfo;
} //视图文件
var viewFiles = getFileInfoFn(viewPath);
//replaceConfig 在切换dev、pro环境时需要替换文件路径的视图文件配置
//gruntImportReg 替换的正则
var gruntImportReg = /<!--\s*grunt-import-\w+\s+\w+\s*-->[\s\S]*?<!--\s*\/grunt-import\s*-->/ig;
var gruntImportItemReg = /(<!--\s*grunt-import-(\w+)\s+(\w+)\s*-->)([\s\S]*?)(<!--\s*\/grunt-import\s*-->)/i;
var replaceConfig = {
'dist':{
options: {
replacements: [
{
pattern: gruntImportReg,
replacement: function(matchStr){
//搜索合并压缩的最新文件
var fileInfo = getFileInfoFn('/Js/dest').concat(getFileInfoFn('Css/dest'));
fileInfo = fileInfo.sort(function(a, b){
return a['cTime'] - b['cTime'];
})
for(var i in staticConfig){
var proFile = staticConfig[i]['pro'].split('/');
proFile = proFile[proFile.length -1].replace(/\./g,'\\.');
fileInfo.forEach(function(v, k){
if(new RegExp("\\."+proFile).test(v['file'])){
staticConfig[i]['pro'] = v['file'];
return false;
}
})
} gruntImportItemReg.lastIndex = 0;
var matchItem = matchStr.match(gruntImportItemReg);
var files = [], importLink = '',
ret = matchItem[1]+'\n';
if(isDev){
files = staticConfig[matchItem[3]]['dev'];
}else{
files = [staticConfig[matchItem[3]]['pro']];
}
if(matchItem[2] == 'js'){
importLink = jsLink;
}else if(matchItem[2] == 'css'){
importLink = cssLink;
}
files.forEach(function(v, k){
ret += importLink.replace('importUrl', v);
ret += '\n';
});
ret += matchItem[5];
return ret;
}
}
]
},
files:{}
}
};
viewFiles.forEach(function(v, k){
replaceConfig['dist']['files'][v['file']] = v['file'];
});
//grunt 配置
grunt.initConfig({
'pkg' : grunt.file.readJSON('package.json'),
'concat' : concatConfig, //合并任务
'uglify' : uglifyJsConfig, //uglify js 压缩,
'cssmin': cssminConfig, //css 压缩,
'clean': {
test: ['tmp'] //创建的临时文件
},
'jshint': {
js: ['Js/*.js', 'Js/**/*.js','Js/**/**/*.js']
},
'string-replace':replaceConfig, //替换路径
'watch': {
scripts: {
files: ['Js/*.js', 'Js/**/*.js','Js/**/**/*.js'],
tasks: ['jshint']
}
},
'rev': {
files: {
src: proFiles
}
}
}); // loadNpmTasks
grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-css');
//clear
grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-jshint'); //dev、production grunt.loadNpmTasks('grunt-string-replace'); grunt.loadNpmTasks('grunt-contrib-watch') grunt.loadNpmTasks('grunt-rev'); //注册任务: // 默认任务
grunt.registerTask('default', ['concat', 'uglify','cssmin','clean']); grunt.registerTask('jsHint', ['jshint']); grunt.registerTask('watch', ['watch']); //根据文件内容生产文件
grunt.registerTask('setCacheFile',['rev']);
//切换dev pro 环境
grunt.registerTask('transfer',['string-replace']); grunt.registerTask('quick', ['default', 'setCacheFile', 'transfer']); };

package.json:

 {
"name": "test2",
"version": "0.1.0",
"author": "bossliu",
"homepage": "###",
"devDependencies": {
"grunt": "~0.4.0",
"grunt-contrib-clean":"~0.4.0rc5",
"grunt-contrib-jshint": "~0.1.1rc5",
"grunt-contrib-uglify": "~0.1.2",
"grunt-contrib-concat": "~0.1.1",
"grunt-string-replace":"~0.2.7",
"grunt-contrib-watch":"~0.6.1",
"grunt-rev":"~0.1.0",
"grunt-css": ">0.0.0"
}
}

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环境切换的更多相关文章

  1. vue-新建项目-构建-打包-环境切换

    一.新建项目 二.运行 npm install npm run start 三.多环境切换 踩坑后总结的方法.. 首先看到package.json 前面的参数都是命令.比如“start”的意思就是np ...

  2. spring boot--日志、开发和生产环境切换、自定义配置(环境变量)

    Spring Boot日志常用配置: # 日志输出的地址:Spring Boot默认并没有进行文件输出,只在控制台中进行了打印 logging.file=/home/zhou # 日志级别 debug ...

  3. 微服务-springboot多环境配置(开发生产测试环境切换)

    springboot根据spring.profiles.active会去寻找应该加载开发环境配置还是生产环境配置 application.properties #生产环境,开发环境,测试环境切换 pr ...

  4. SpringBoot-多环境切换相关(六)

    多环境切换 profile是Spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境: 方式一:多配置文件 我们在主配置文件编写的时候,文件名可以是 applicat ...

  5. SpringBoot-03-JSR303数据校验和多环境切换

    3.3 JSR303数据校验 先看如何使用 ​ Springboot中可以用@Validated来校验数据,如果数据异常则统一抛出异常,方便异常中心统一处理. ​ 这里我们写个注解让name只支持Em ...

  6. SpringBoot配置文件-多环境切换

    profile是Spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境: 多个文件-配置多环境: 需要多个配置文件,文件名可以是 application-{prof ...

  7. 史上最全Spring Cloud Alibaba--Nacos教程(涵盖负载均衡、配置管理、多环境切换、配置共享/刷新、灰度、集群)

    能够实现Nacos安装 基于Nacos能实现应用负载均衡 能基于Nacos实现配置管理 配置管理 负载均衡 多环境切换 配置共享 配置刷新 灰度发布 掌握Nacos集群部署 1 Nacos安装 Nac ...

  8. windows本地搭建grunt前端项目构建环境

    初学,目前对grunt的理解和需求仅在于简单的文件合并.压缩.语法检查,其强大功能还有待研究. 安装前环境准备 (1)grunt依赖nodejs运行环境,所以要玩grunt得先把nodejs安装好,n ...

  9. 简单实现计算机上多个jdk环境切换

    实现多个jdk环境切换,大致有两种方式 安装两个jdk,并配置相应的环境变量,在java的控制面板中修改设置 非主要的jdk仅仅是用来测试,并不常用,故只要让ide配置对应的jdk位置就可以了,属于懒 ...

随机推荐

  1. linux inode已满解决方法

    今天login server的一个网站,发现login后没有生成session.根据以往经验,一般是空间已满导致session文件生成失败. df -h Filesystem Size Used Av ...

  2. PHPCMSv9 更改后台地址(测试)

    最新发布的PHPCMS V9由于采用了MVC的设计模式,所以它的后台访问地址是固定的,虽然可以通过修改路由配置文件来实现修改,但每次都修改路由配置文件对于我来说有点麻烦了,而且一不小心就会出错.这里使 ...

  3. Css3做的旋转显示文字和角度的变化

    Css: .spinner{ width:245px; height:245px; position:relative;}.coly{ border-radius:130px; font-size:1 ...

  4. DeDe缩略图路径的修改

    今天在使用dedecms的时候,遇到的一点小问题,移站的时候缩略图不显示.然后就去百度搜了一下,出来的都是千篇一律. 因为一个网站里面缩略图太多,手动比较慢而已费时间,  俗话说的好“时间就是生命” ...

  5. SJA1000寄存器设置

    在设置CAN控制器SJA1000的输出控制寄存器(OCR)时,由于电路图中只用到了TX0和RX0,所以只考虑OCTP0,OCTN0,OCPOL0.这里设置成了010.然后查了一下配置的表,如下所示: ...

  6. 从汇编来看c语言之变量

    1.基础研究 对如图程序进行编译连接,再用debug加载. 我们在偏移地址1fa处查看main函数的内容: 执行到1fd处,发现n的偏移地址为01a6,段地址存储在ds寄存器里,为07c4. 再查看函 ...

  7. AdHoc发布时出现重复Provisioning Profile的解决方案

    当在developer.apple.com更新Provisioning Profile(添加新机器)后,下载到本地,双击载入xcode,运行时没问题.但如果用adhoc发布,可能会发现重复的provi ...

  8. PCB Layout爬电距离、电气间隙的确定

    爬电距离的确定:首先需要确定绝缘的种类:基本绝缘:一次电路与保护地工作绝缘 ① :一次电路内部:二次电路内部工作绝缘 ② :输入部分(输入继电器之前)内部,二次电路与保护地加强绝缘:一次电路与二次电路 ...

  9. php5,Apache在windows 7环境搭建

    主要是参考以下文章: http://www.cnblogs.com/Yogurshine/archive/2013/05/24/3097343.html http://jingyan.baidu.co ...

  10. COJ 0801 非传统题(一)

    非传统题(一) 难度级别:A: 运行时间限制:1000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 大家好!我是COJ第一道非传统题,是不是感觉非常的excite ...