担心ECMAScript 6来之太晚? 不待! nodejs 0.10起启ECMAScript 6无论是督二脉

第一,我们得一个ECMAScript 6 ->
ECMAScript5的编译器,目前很之多。这里推荐Google出品的traceur-compiler

此间有一个编译器列表:
es6-tools。

原理

应用这些编译器,会赞助你拿现行用ECMAScript
6告知法编写的javascript文件,编译生成ECMAScript 5语法的静态文件。

要你是coffeescript的使用者,那就是怪熟悉这种艺术,类似coffeescript语法编译成javascript文件。

为凡静态文件,所以未会见在运作时更编译的性能问题。

除此以外,也供了运转时编译,帮助你一直测试ECMAScript 6告知法编写的文件。

traceur-compiler 使用入门

首先自己怀念说,需求决定了技能之需。因为一个品类的复杂度,我急于用yield
和生成器来简化异步判断。因为对express和koa那种java式的纯粹面向对象深深的反感的久,(对JAVA
C++
和纯粹面向对象的批判,已经是卡内基·梅隆大学”反模块化的又是相反并行的”的屡见不鲜便饭),我于长期之累中,短日编写出了ROCORE,并火速测试优化,迭代进了0.2.17本子(事实上我现使用的本又发生矣初的变更,一个净使用ECMAScript
6语法beta的0.3.1版本)。

以能迅速配合nodejs
0.10,我找到了6to5,
traceur-compiler,并选择了traceur-compiler用作编译器。

  1. 安装

    npm install -g traceur

  2. 编写ECMAScript 6语法的文件

编排而的ECMAScript 6文书,例如ec6.js文件:

    // ec6.js

    let a = 1;

    function* g() {
        yield 100;
        console.log(a + 100);
    }

    var it = g();
    it.next();
    it.next();  
  1. 编译生成ECMAScript 5语法的公文

使用traceur-compiler编译文件,生成ECMAScript文件,比如名ec5.js
$ traceur ec6.js --out ec5.js --modules=commonjs

开拓得到的ec5.js文件,你见面看出如下的代码:
 
“use strict”;
var $__0 = $traceurRuntime.initGeneratorFunction(g);
var __moduleName = “tt.js”;
var a = 1;
function g() {
return $traceurRuntime.createGeneratorInstance(function($ctx) {
while (true)
switch ($ctx.state) {
case 0:
$ctx.state = 2;
return 100;
case 2:
$ctx.maybeThrow();
$ctx.state = 4;
break;
case 4:
console.log(a + 100);
$ctx.state = -2;
break;
default:
return $ctx.end();
}
}, $__0, this);
}
var it = g();
it.next();
it.next();

  1. 运行生成的ECMAScript 5文本

    除开全局安装,你还用另外复制一份,放到你的类node_modules中,作为项目依赖。模块名是traceur。

    若运行此ec5.js文件,还需引入traceur模块。在文书上部,加上

    require('traceur');
    

    接下来保留。运行
    $ node ec5.js

    OK,输出101. 现行以nodejs 0.10遭遇,能够好的运转此文件。

  2. 总结一下经过

    编写ECMAScript 6文本 -> 编译,生成ECMAScript 5文件 ->
    修改ECMAScript 5文件,在绝上方加入require(‘traceur’)保存 ->
    运行ECMAScript 5文件。

    在实际上使用时,只待以app.js(或者server.js)主文件被入require(‘traceur’)既可以。

批量编译

咱的花色文件可是有大气之文本,总不克一个一个下手编译吧?!OK。这里发生一个批量编译的剧本程序,复制然后保留至一个js文件里,设置要编译的主目录和要出口的目标目录,ta会拿主目录的有js文件编译到目标目录,并且支持递归内部文件,非js文件给形容不变换的复制到目标目录。

此外,建议单独编译复制lib目中之公文,其他目录的文件手动复制到目标目录。

// compile.js

var traceur = require('traceur');
var fs = require('fs');
var path = require('path');
var PATH_SOURCE = '/home/king/box-fork/dist';
var PATH_TARGET = '/home/king/box-fork-compile';

function readSync(paths, i, f) {
    if (i >= 0 && i < paths.length) {
        var stats = fs.statSync(paths[i]);
        if (stats.isFile()) {
            f('file', paths[i]);
            return readSync(paths, ++i, f);
        } else if (stats.isDirectory()) {
            var newPaths = fs.readdirSync(paths[i]).map(function (pathname) {
                return path.join(paths[i], pathname);
            });
            f('directory', paths[i]);
            return readSync(paths.slice(0, i).concat(newPaths, 
                                                     paths.slice(i + 1)), 
                            i, f);
        } else {
            return readSync(paths.slice(0, i).concat(paths.slice(i + 1)), 
                            i, f);
        }
    } else {
        return paths;
    }
}

readSync([PATH_SOURCE], 0, function (type, pathname) {
    var newPath = path.join(PATH_TARGET, pathname.replace(new RegExp('^' + source.replace(/\//g,'\/').replace(/\\/g,'\\')), ''));
    if (type === 'file') {
        if (path.extname(pathname) !== '.js') {
            console.log('copy %s %s', pathname, newPath);
            fs.writeFileSync(newPath, fs.readFileSync(pathname));
            return;
        }

        console.log('traceur %s %s', pathname, newPath);
        var src = fs.readFileSync(pathname, {encoding:'utf8'});
        var options = {};
        var compiled = traceur.compile(src, options);

        fs.writeFileSync(newPath, compiled, {encoding:'utf8'});
    } 
    if (type === 'directory') {
        console.log('mkdir %s', newPath);
        fs.mkdirSync(newPath);
    }
});

运行时编译和单元测试

当档次开支过程,我们只是免思写了一个文本就编译一破,所以我们尚产生个办法,在开发的时刻用运行时编译。等任何付出了,测试结束,然后同不良编译,上线。

于品种立一个test-traceur.js文件

// 这段代码请原样复制
require('traceur').require.makeDefault(function(filename) {
    return filename.indexOf('node_modules') === -1;
});

// 这里引入需要测试的目标文件
require('./test/my.js');

下一场,运行测试:
$ node test-traceur.js

次第即使会见自行编译my.js和my.js所负之文书,并且运行。

test-traceur.js文件中的写法是要这样的,只能引入一个测试文件,这是由trace-compiler设定的,具体可参考其文档。

自我有一个批量测试脚本,如果您需要的话:

  • 起两独公文test-traceur.js, test-all.js

  • test-traceur.js:

require('traceur').require.makeDefault(function(filename) {
    return filename.indexOf('node_modules') === -1;
});

require(process.argv[3]);
  • test-all.js:

var fs = require('fs');
var path = require('path');
var child_process = require('child_process');
var ROOT = path.join(__dirname, '../test');
var ROOT_TRACE = path.join(__dirname, 'test-traceur.js');


(function test(paths, i) { 
    if (i >= 0 && i < paths.length) {
        var stats = fs.statSync(paths[i]);
        if (stats.isFile()) {
            if (path.extname(paths[i]) === '.js') { 
                console.log('\ntest: %s', paths[i]);
                child_process.exec('node ' + ROOT_TRACE + ' -f ' + paths[i], function (err, stdout, stderr) {
                    if (err) {
                        throw err;
                    }
                    console.log('OK: %s', paths[i]);
                    test(paths, i + 1);
                });
            } else {
                test(paths, i + 1);
            }
        } else if (stats.isDirectory()) {
            var newPaths = fs.readdirSync(paths[i]).map(function (pathname) {
                return path.join(paths[i], pathname);
            }); 
            test(paths.slice(0, i).concat(newPaths, paths.slice(i + 1)), i);
        } else {
            test(paths.slice(0, i).concat(paths.slice(i + 1)), i);
        }
    } else {
        console.log('\ncomplete\n');
    }
} ([ROOT], 0));

test-all.js文件遭受,修改ROOT为要测试目录,运行
$ node test-all.js

于测试目录的文本就见面相继运行。(他们跟她们仗之公文了可是ECMAScrip
6语法的文件)

终极,推荐你以ROCORE简化你的异步开发

即便你莫思改变你的express或者其它,你仍能自ROCORE获得救助,比如中的scc和mcc这片单时刻可用的异步工具:

var R = require('rocore');
var fs = require('fs');

R.scc(function* (ynext) {
    var [err, data] = yield fs.readFile(__dirname + 'my.conf', {encoding:'utf8'}, ynext);
    if (err) throw err;
    yield fs.writeFile(__dirname + 'new.conf', data, {encoding:'utf8'}, ynext);
    console.log('done');
});

var R = require('rocore');
var pool = require('mysql').createPool(/*配置*/);

R.scc(function* (ynext) {
    var {employee, order, position} = yield R.mcc(function* (ynext) {
        yield pool.query('SELECT * FROM employee', ynext('employee'));
        yield pool.query('SELECT * FROM order', ynext('order'));
        yield pool.query('SELECT * FROM position', ynext('position'));
    }, ynext);
    console.log(employee);
    console.log(order);
    console.log(position);
});