在项目开发过程中和发布阶段需要在开发环境(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. SCM管理器

    OpenSCManager 打开SCM管理器 CloseServiceHandle 关闭句柄 CreateService 创建服务 OpenService 打开服务 ControlService 控制 ...

  2. ubuntu新建某个目录的快捷方式到桌面,有三种方法

    0. ln -sf /home/wjs ~/Desktop (wjs是我的登录用户名). 1. 在文件浏览器里 ,目录的右键有个make link 或 创建链接 ,然后cut or copy 到 桌面 ...

  3. Android 之形状Drawable

    形状Drawable资源允许使用 <shape>标记指定基本形状的尺寸.背景.轮廓线,从而定义这些基本形状. 每个形状都包含一个类型(通过shape属性指定).定义该形状尺寸的属性,以及指 ...

  4. FE: CSS固定图片显示大小及GitHub Pages在线演示

    CSS固定图片显示大小 分析 假设图片区域的大小固定为250×300px,那么我们可以写出如下的样式 .picture-area { width: 250px; height: 300px; marg ...

  5. 【HDOJ】4544 湫湫系列故事——消灭兔子

    贪心,普通贪心两层循环TLE了,然后用优先级队列维护内层. #include <iostream> #include <cstdio> #include <cstring ...

  6. BZOJ1634: [Usaco2007 Jan]Protecting the Flowers 护花

    1634: [Usaco2007 Jan]Protecting the Flowers 护花 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 448  So ...

  7. android SimpleCursorAdapter的使用

    String[] fields=new String[]{"foodname","price","taste","stuff&qu ...

  8. HDU5140---Hun Gui Wei Company (主席树)

    主席树太强大了,,如果仅仅用来求第k大就太屈才了..貌似和HDU4605差不多,那个是在图上根据点的顺序建立主席树,这个是根据年龄大小 或者等级高低建立主席树. 题意 大致就是一个二维区间的求和,但是 ...

  9. windows 删除服务命令

    在dos窗口下执行 sc delete  服务名( 例如 mysql) C:\Program Files\MySQL\MySQL Server 5.6\

  10. (转)在Android的webview中定制js的alert,confirm和prompt对话框的方法

    1.首先继承android.webkit.WebChromeClient实现MyWebChromeClient. 2.在MyWebChromeClient.java中覆盖onJsAlert,onJsC ...