我使用angularjs写网页,当逻辑复杂的时候,需要写很多js代码。我对js一直比较怕,因为有这几个问题困扰着我:
所以我一直想找一种语言可以代替js,动静态都行,但要能有效解决上面的几个问题。
我考察了以下几种:
它们都是可以生成js代码在浏览器端运行的。
最终我的选择是Haxejs,这里讲一下选择的原因以及一些示例供参考。
关于Haxe,对于普通的使用者我并不是很推荐,因为它现在有以下问题:
但这并不能掩盖Haxe的优点:
通过检查haxe生成的js代码,可以看出使用了最常用的js用法,所以兼容性不是问题。同时它也内置了对jquery的支持,生成的是jquery的调用,也可以做到浏览器的兼容。
其实我以前就曾经试过Haxe,但当时感觉比较失望,因为相比dart,似乎并没有太多吸引人的地方。但是我看到了杨博的一篇文章:Haxe+Node.js+continuation打造高性能高开发效率服务器架构(下)后,改变了自己的看法,因为如果能掌握haxe的macro,就有可能实现出以前做不到的功能。比如杨博的那个库,就是我以前研究nodejs时很想要的一个东西。
所以真正让我想用haxe的,也是因为haxe的macro,然后才发现了更多的优点(以及不爽之处)。因为Angularjs特殊的流程,使得它无法很好利用类结构带来的优势。比如这段代码,可以看作是angularjs的标准Controller写法:
function Ctrl($scope) {
$scope.name = "Freewind";
$scope.hello = function() {
alert($scope.name);
}
$scope.method1 = function() {}
$scope.method2 = function() {}
$scope.method3 = function() {}
$scope.method4 = function() {}
$scope.method5 = function() {}
}
对于Angularjs来说,这个Ctrl函数,仅仅是用来在调用一次后,对传入的$scope进行改变,比如增加属性和方法等,然后Ctrl和$scope就没关系了。如果想用引入类,大约是这样的:
class Ctrl {
public function new(scope:Scope) {
scope.name = "Freewind";
scope.hello = function() {
alert(scope.name);
}
scope.method1 = function() {}
scope.method2 = function() {}
scope.method3 = function() {}
scope.method4 = function() {}
scope.method5 = function() {}
}
}
typedef Scope = {
name:String,
hello:Void->Void,
method1: Void->Void,
method2: Void->Void,
method3: Void->Void,
method4: Void->Void,
method5: Void->Void
}
可以看出来,这段代码跟前面的差不多,虽然引入了class,但却没有办法利用上class的字段、方法等结构,还是全部在一个new函数里操作。同时为了利用编译器的检查,还要另外为scope定义一个type hint,既繁琐又不好看。(虽然看起来有点繁琐,但对于很长的javascript代码,这样做还是有莫大的好处。因为毕竟只有少数代码是用来定义类型的,大部分代码可以受益)
如果我们的目的仅仅是这样,那么使用dart/typescript/haxe中的任意一种,都可以做到。但我希望可以写成这样:
class Scope {
public var name:String = "Freewind";
public function hello() { alert(this.name); }
public function method1() {}
public function method2() {}
public function method3() {}
public function method4() {}
public function method5() {}
}
看起来清楚多了,但问题是,无法实现。
这时候我想到haxe的宏(macro)不知道能不能派上用场。因为在杨博的例子中,可以看出macro的威力是巨大的,它可以把平行的代码变成嵌套的,这个改变相当的大,而不是局部优化。我能不能也写一个宏,但这个Scope类上定义的字段和方法,统统转移给传入的那个scope变量呢?这样我们写出来的是具有类结构的haxe代码,而产生的是angularjs需要的代码,皆大欢喜。
于是我开始了我痛苦而又有挑战性的macro之旅。此处省略三天废寝忘食的描述,并且对back2dos和simon等多位国际友人及haxe前辈表达深深的谢意,没有你们的帮助,我简直寸步难行。
最后的结果是,我差一点点就实现了这个功能。因为在此期间,发现了haxe编译器的一个bug:http://code.google.com/p/haxe/issues/detail?id=1401,也正是这个bug,让我的功能没法实现。因为在编译期haxe对于类型的推断有问题,导致在改变代码结构的时候,无法正确的推断出某些方法的类型,编译不通过或者生成的js代码有误。
我的代码放在:https://github.com/freewind/HaxeMacroQkdny,有兴趣的同学可关注。我现在只能等待官方修正了这个bug后,才能继续尝试。
如果这个功能实现了,我们就可以实现这样的效果:
haxe代码
@AngularScope("$scope")
class Ctrl {
@Inject("$http") private var http;
public var name = "freewind";
public function hello() {
trace(name);
}
}
Js代码
function Ctrl($scope, $http) {
$scope.name = "freewind";
$scope.hello = function() {
console.log($scope.name);
}
}
现在所能做的,只有等待了。如果这个功能实现的话,使用haxejs来开发angularjs就很爽了。
这个功能已经实现了,使用了一种与这里描述的不一样的方式,绕开了那个bug。