Grunt是一款基于node的javascript任务管理器工具。我们的项目使用Grunt实现项目自动化打包,以及后续的持续集成。Grunt如何使用,本文不详细介绍(其实是不会-_-!),详见《【grunt整合版】30分钟学会使用grunt打包前端代码》。
1、打包思路
项目从开发态到发布态,需要做哪些事情?一、代码检查,包括代码风格检查、js静态代码检查、单元测试、代码静态分析。二、文件预处理:包括sass转css、seajs模块转换、文件变量宏替换。三、文件拷贝、压缩、合并。所以处理流程也很简单。如下图所示:
2、代码风格检查
Grunt中代码风格检查插件很多,我们的项目采用的是jscs-dev/grunt-jscs。另外,为了生成代码风格检查报告,还需要aj-dev/jscs-html-reporter报表插件。配置参数如下:
jscs: {
src: ['script/module/**/*.js', '!script/module/**/tpl/*.js'],
options: {
config: '.jscsrc',
reporter: 'node_modules/jscs-html-reporter/jscs-html-reporter.js',
reporterOutput: 'report/jscs/index.html'
}
},
其中,.jscsrc是代码风格规则文件。参考配置如下:
{
"disallowSpacesInNamedFunctionExpression": {
"beforeOpeningRoundBrace": true
},
"disallowSpacesInFunctionExpression": {
"beforeOpeningRoundBrace": true
},
"disallowSpacesInAnonymousFunctionExpression": {
"beforeOpeningRoundBrace": true
},
"disallowSpacesInFunctionDeclaration": {
"beforeOpeningRoundBrace": true
},
"disallowEmptyBlocks": true,
"disallowSpacesInsideArrayBrackets": true,
"disallowSpacesInsideParentheses": true,
"disallowQuotedKeysInObjects": true,
"disallowSpaceAfterObjectKeys": true,
"disallowSpaceAfterPrefixUnaryOperators": true,
"disallowSpaceBeforePostfixUnaryOperators": true,
"disallowSpaceBeforeBinaryOperators": [
","
],
"disallowMixedSpacesAndTabs": true,
"disallowTrailingWhitespace": true,
"disallowTrailingComma": true,
"disallowYodaConditions": true,
"disallowKeywords": [ "with" ],
"disallowMultipleLineBreaks": true,
"requireSpaceBeforeBlockStatements": true,
"requireParenthesesAroundIIFE": true,
"requireSpacesInConditionalExpression": true,
"requireMultipleVarDecl": "onevar",
"requireBlocksOnNewline": 1,
"requireCommaBeforeLineBreak": true,
"requireSpaceBeforeBinaryOperators": true,
"requireSpaceAfterBinaryOperators": true,
"requireCamelCaseOrUpperCaseIdentifiers": true,
"requireLineFeedAtFileEnd": true,
"requireCapitalizedConstructors": true,
"requireDotNotation": true,
"requireCurlyBraces": [
"do"
],
"requireSpaceAfterKeywords": [
"if",
"else",
"for",
"while",
"do",
"switch",
"case",
"return",
"try",
"catch",
"typeof"
],
"safeContextKeyword": "_this",
"validateLineBreaks": "LF",
"validateQuoteMarks": "'",
"validateIndentation": 2
}
生成报表如下图所示:
3、js代码静态检查
js代码静态检查使用gruntjs/grunt-contrib-jshint插件。另外,为了生成静态检查报告,还需要adrianpietka/jshint-html-reporter报表插件。参数配置如下:
其中,.jshintrc是静态检查规则文件。参考配置如下:
{
"asi" : true,
"browser" : true,
"eqeqeq" : false,
"eqnull" : true,
"es3" : true,
"expr" : true,
"jquery" : true,
"qunit" : true,
"latedef" : "nofunc",
"nonbsp" : true,
"strict" : false,
"undef" : true,
"unused" : "vars",
"sub" : true,
"multistr" : true,
"loopfunc" : true
}
生成报表如下图所示:
4、单元测试
我们的项目中,单元测试采用qunit。所以需要下载grunt的qunit插件:jquery/qunit。另外,为了能够在jenkins中显示单元测试报告,还需要qunit转junit插件:sbrandwoo/grunt-qunit-junit。参数配置如下:
// 单元测试
qunit: {
script: ['test/**/*.html']
},
qunit_junit: {
options: {
dest: 'report/qunit',
classNamer: function (moduleName, url) {
var cn = url.replace(/\.html(.*)$/, '').replace(/[\\|\/]/g, '.');
var index = cn.lastIndexOf('.');
cn = cn.substr(index+1);
return cn;
},
testNamer: function (testName, moduleName, url) {
var tn = url.replace(/\.html(.*)$/, '').replace(/[\\|\/]/g, '.');
var index = tn.lastIndexOf('.');
tn = tn.substring(0, index);
// return moduleName.replace(/[\\|\/]/g, '.').replace(/\s+/g, '_');
return tn;
}
}
},
生成的报表如下图所示:
点击失败用例,可以查看详情:
单元测试完成后,还要生成覆盖率报告,这一部分内容坑比较多,另写一篇文章详细介绍。
5、js代码静态分析。
js代码静态分析需下载插件:es-analysis/plato。参数配件比较简单,如下图所示:
plato: {
your_task: {
files: {
'report/plato': ['script/**/*.js', '!script/**/tpl/*.js']
}
}
}
生成报表如下图所示:
Plato中通过一系列指标来衡量一个项目的代码质量情况。下面详细介绍一下各指标的意义。
5.1、Total Lines
总代码行,表示文件中的代码总行数。
5.2、Avarage Lines
平均代码行=总代码行/文件数。
5.3、Maintainability
Maintainability Index = MAX(0,(171 - 5.2 * log(Halstead Volume) - 0.23 * (Cyclomatic Complexity) - 16.2 * log(Lines of Code))*100 /171)。
可维护性指标是0-100之间的数字,越高越容易维护。其中:
20-100:易于维护
10-19:较难维护
0-9:很难维护
提高可维护性的方法有:
降低圈复杂度
降低单文件代码行数
参考:http://blogs.msdn.com/b/zainnab/archive/2011/05/26/code-metrics-maintainability-index.aspx
5.4、Average Maintainability
平均可维护性=每个文件的可维护性指数求平均。
5.5、Halstead complexity measures
Halstead复杂度测量算法,如下图所示:
参考:https://en.wikipedia.org/wiki/Halstead_complexity_measures
5.6、Estimated errors in implementation
潜在bug数估计,算法如下图所示:
降低潜在bug数的方法:
降低代码量。
参考:https://en.wikipedia.org/wiki/Halstead_complexity_measures
5.7、Lint errors
jsLint错误数。
5.8、Difficulty
文件中去重操作数越多,越难;重复操作符越多,越难。
参考:https://en.wikipedia.org/wiki/Halstead_complexity_measures
5.9、Complexity
圈复杂度,表示代码块中所有可能的路径数。圈复杂度越低越好。
降低圈复杂度的方法:
减少分支数。
5.10、Function weight
函数权重。有如下几种分类方式:
By Complexity:按圈复杂度统计。
BySLOC:按照SLOC/LSLOC(源代码行/逻辑代码行)统计。
SLOC:物理代码行,统计物理行数,包括注释、空行等。
LSLOC:逻辑代码行,统计语句数。
参考:https://en.wikipedia.org/wiki/Source_lines_of_code
6、css、js转换
因为项目中有用到sass和seajs。所以,打包的时候需要把sass文件转为css文件,用到gruntjs/grunt-contrib-sass插件,具体配置如下:
sass: {
dist: {
options: {
style: "compressed",
sourcemap: 'none'
},
files: [{
expand: true,
cwd: 'theme/default/scss',
src: ['**/*.scss'],
dest: 'dist/theme/default/css',
ext: '.css'
}, {
expand: true,
cwd: 'theme/affairs/scss',
src: ['**/*.scss'],
dest: 'dist/theme/affairs/css',
ext: '.css'
}]
}
}
CMD规范规定一个文件内只能定义一个模块。因此多个模块文件不能合并为一个文件。为了解决这个问题,需要引入spmjs/grunt-cmd-transport插件。该插件可以为seajs模块分配id和提取依赖项,这样就能够支持多模块文件合并。相应配置如下:
// 转化
transport: {
options: {
debug: false,
idleading: 'dist/',
alias: {
'$': '$',
'helper': 'helper',
'cache': 'cache',
'fun': 'fun',
'kissy': 'kissy'
}
},
script: {
options: {
idleading: 'script/module/'
},
files: [{
expand: true,
cwd: 'script/module/',
src: ['**/*.js', '!**/index.js', '!**/tpl/*.dev.js'],
dest: '.build/script/module'
}]
}
}
7、文件删除、合并、拷贝、压缩。
需要用到gruntjs/grunt-contrib-clean、spmjs/grunt-cmd-concat、gruntjs/grunt-contrib-copy和gruntjs/grunt-contrib-uglify。这几个插件很常用,不解释。