agile4js

可通过添加微信帐号geek32,或者微博帐号姜志辉iS与我讨论

Agile for JS

Clean Code:编写干净整洁的JS代码

发现异味

怎么写出干净整洁的代码呢?嗯。这个问题我们可以说上一整天...

首先,咱得有分辨异味的能力。

JS更是如此。Douglas Crockford专门为此写了一本《JavaScript:The Good Parts》.在这本书里不仅仅是提到了JavaScript中的优美特性,而且还罗列出了JavaScript中的毒瘤和糟粕。JSLint是老道提供的一个JavaScript的代码质量检查工具。它读取源文本进行扫描。被发现的问题往往是语法错误,也有一些代码风格及结构上的问题。

它不会证明我们的程序是正确的,只是提供了一种建议。

JSHint是JSLint的一个分支,他比JSLint更加的便捷。可以通过npm安装:

npm install jshint -g

安装完成后,即可以使用jshint来检测代码(jshint xxx.js,或者jshint .)。

如果使用Sublime Text,可以安装两个插件:

测试驱动开发

编程风格有很多种:祈祷式编程、撞大运式编程、屠宰式编程...

我最喜欢的编程风格是测试驱动开发。从User Story,到BDD,再到TDD一气呵成。

Mocha无论是名称还是其自身所散发的气质都让我爱不释手。可通过npm安装:

npm install -g mocha

TDD风格

可以新建一个js文件,然后使用mocha xxx.js测试它:

var assert = require("assert");
var Class = {
    define: function(options) {
        var klass = function() {};
        for (var prop in options) {
            klass.prototype[prop] = options[prop];
        }
        return klass;
    }
};
describe('Class', function() {
    describe('define()', function() {
        it('should return class when use Class.define', function() {
            var Person = Class.define({
                sayHello: function() {
                    return 'hello';
                }
            });
            var jobs = new Person();
            assert.equal('hello', jobs.sayHello());
        });
    });
});

BDD风格

mocha同样支持BDD风格。BDD风格的插件也很多,推荐should:

var assert = require("assert"),
    should = require('should');

var Class = {
    define: function(options) {
        var klass = function() {};
        for (var prop in options) {
            klass.prototype[prop] = options[prop];
        }
        return klass;
    }
};
describe('Class', function() {
    describe('define()', function() {
        it('should return class when use Class.define', function() {
            var Person = Class.define({
                sayHello: function() {
                    return 'hello';
                }
            });
            var jobs = new Person();
            jobs.sayHello().should.eql('hello');
        });
    });
});

使用-R spec可以查看spec风格的测试报告mocha -R spec xxx.js

覆盖率测试

配合mocha使用blanket进行覆盖率测试:

mocha --require blanket -R html-cov>coverage.html xxx.js

使用blanket,需要配置一下package.json:

{
    "config":{
        "blanket":{
            "pattern":"xxx.js"
        }
    }
}

运行mocha,测试的结果会以html-cov的形式输出到converage.html中。

Grunt

很多人都应该使用过Jenkins+Maven的CI组合。Grunt更像Gradle, 是一个基于JavaScript任务的命令行构建工具,可以用来执行合并、压缩和校验,运行单元测试以及启动静态服务器等任务。

可以使用npm来安装命令行工具:

npm install -g grunt-cli

grunt-cli,是指Grunt's command line interface。这里安装的是命令行而不是Grunt。在执行时,Grunt会自动寻找当前目录的gruntfile.js文件。可以手写这个文件,也可以通过grunt-init引导生成。

安装grunt-init:

npm install -g grunt-init

可选的模板很多,gruntfile生成模板是最常用的:

git clone https://github.com/gruntjs/grunt-init-gruntfile.git ~/.grunt-init/gruntfile

使用grunt-init gruntfile依据引导生成gruntfile.js文件。

grunt为我们默认提供了一些常见的插件:

"devDependencies": {
    "grunt": "~0.4.2",
    "grunt-contrib-jshint": "~0.7.2",
    "grunt-contrib-watch": "~0.5.3",
    "grunt-contrib-qunit": "~0.3.0",
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-uglify": "~0.2.7"
}

更新package.json,使用npm install安装依赖。

合并、压缩

尽量减少网络IO带来的负担。

合并多个文件,减少网络的请求数量。通过精简压缩减小每次传输的大小。UglifyJS被认为是第一个基于NodeJS的压缩工具,UglifyJS是由Mihai Bazon用JavaScript编写的基于JavaScript的解析器。它会去除注释和额外的空格,替换变量名,合并var表达式,并进行一些其他方式的优化。

可以使用npm install uglify-js -g安装uglify-js。

使用grunt,默认会选择uglify-js作为文件压缩工具。如果我们使用grunt-init自动生成了gruntfile文件,会发现关于合并、压缩的部分阅读起来没有什么难度,根据项目的要求自行修改即可。

更改gruntfile的结尾:grunt.registerTask('default', ['concat', 'uglify']);,将concat和uglify的任务节点注册为默认的grunt文件。当在命令行中使用grunt,会自动执行合并和压缩任务。

代码检查

可以在grunt中配置jshint的选项。并加jshint添加到默认的grunt任务:

grunt.registerTask('default', ['jshint','concat', 'uglify']);

可以使用grunt jshint运行指定任务,也可以直接使用grunt运行一系列js任务。

单元测试

grunt使用qunit作为默认的单元测试模块。这让我有些诧异。虽然qunit非常不错,不过业界...好吧。我,更喜欢使用mocha作为单元测试框架。

可以使用grunt-mocha-test模块来代替qunit测试模块。

grunt-mocha-test添加到package.json的依赖模块中:

"devDependencies": {
    "grunt": "~0.4.2",
    "grunt-contrib-jshint": "~0.7.2",
    "grunt-contrib-watch": "~0.5.3",
    "grunt-contrib-qunit": "~0.3.0",
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-uglify": "~0.2.7",
    “grunt-mocha-test”:"*"
}

通过npm install安装。

注册grunt-mocha-test

使用mochaTest任务替换qunit任务的配置项:

mochaTest: {
  test: {
    options: {
      reporter: 'spec'
    },
    src: ['*.js']
  }
},

并在grunt中调用grunt-mocha-test模块:

 grunt.loadNpmTasks('grunt-mocha-test');

可以使用grunt mochaTest执行单元测试任务。也可以将mochaTest添加到默认的grunt任务中,这样在运行grunt时,会按照顺序执行jshint、mochaTest、concat、uglify四个任务。

grunt.registerTask('default', ['jshint','mochaTest','concat', 'uglify']);

将覆盖率测试添加进mochaTest任务

grunt-mocha-test支持blanket的覆盖率测试配置:

mochaTest: {
  test: {
    options: {
      reporter: 'spec',
      require: 'coverage/blanket'
    },
    src: ['class_test.js']
  },
  coverage: {
    options: {
      reporter: 'html-cov',
      quiet: true,
      captureFile: 'coverage.html'
    },
    src: ['class_test.js']
  }
},

mochaTest.test.options.require选项会请求coverage目录下的blanket.js文件。这需要我们创建coverage目录,并在其中添加blanket.js文件:

require('blanket')({
    pattern: 'class_test.js'
});

这个文件用来取代mocha --require blanket中的blanket。并且通过代码的方式代替在package.json中定义的配置项。如此,在执行grunt命令时,就不需要在package.json中进行配置了。

执行grunt,可以在同级目录中查看到coverage.html测试覆盖率文件。

在创建一个可维护的项目时,使用构建系统仅仅是第一步,接下来是把构建系统集成到一个CI系统中。CI系统是建立在某些操作或者定期间隔的基础上自动运行生成的。这里有一些可以考虑的免费的CI系统:

  • Jenkins
  • Continuum
  • BuildBot
  • Cruise Control
  • Gradle