haXe是高级语言的高级语言,可以将一个haXe程序转换为其它的高级语言,如JS。haXe有很多优点,比如,强类型、泛型、内联、宏、动态语言特性、命名空间……,用haXe写程序比用JS写程序舒服得多。本文尝试使用haXe开发js程序,搭建一个前端mini型mvc例子。

语言:haXe。haXe编译器可以把haXe程序编译成js程序。

开发工具:FlashDevelop

haXe的简介及安装见《html5 canvas 版 hello world! 暨haXe简介》。开发之前,需要修改Flash Develop配置,将Tools->Program Setting->HaXeContext->Disable Compiler-based Completion 设置为 true。否则的话,haXe的自动完成会很痛苦。

image

项目结构如上图。其中:index.html是演示页面。页面中引用的jsTest.js就是本haXe项目所生成的js文件。js程序的入口就是Main.hx中的main方法(后文详述) 。main方法查找本页面中的所有的haXe标签,根据标签的name,分派出不同的Controller出来,然后根据haXe标签的map配置,把haXe所在节点的父节点及兄弟节点中id对应的节点映射到控制器里的字段。这样一来,一个页面可以对应0个、1个或多个Controller,每个页面片段也可以对应0个、1个或多个Controller,具有极大的灵活性。

有map的存在,在Controller那边,可以少写很多代码。map语法为:

(1)不同的map项由“;”分割;

(2)每个map项语法为“元素id:映射到字段的id”,如果省略掉“:”和之后的内容,则使用元素id作为映射字段的id。

这样一来,就可以把客户端的行为封装成强类型的、严格规划好命名空间的、有严格文档的haXe代码。开发前端时,只需要将不同的元素id映射到不同的Controller中即可实现不同的控制行为。

下面看看核心类:

(1)Page.hx

Page类封装了一些常用的方法:

package ;

import js.Dom;

#if hack
import js.HtmlDom;
import js.Document;
import js.HtmlCollection;
#end

import js.Lib;

class Page
{
    public static function getControllers():HtmlCollection<HtmlDom>
    {
        return doc().getElementsByTagName("haXe");
    }
    public static inline function doc():Document
    {
        return Lib.document;
    }
    public static function getById(id:String,node:HtmlDom=null):HtmlDom
    {
        if (id == null || id == '') return null;
        else if (node == null)
        {
            return doc().getElementById(id);
        }
        if (node.id == id) return node;
        else
        {
            if (node.hasChildNodes() == true)
            {
                var item:HtmlDom = node.firstChild;
                while (item != null)
                {
                    if (item.id == id) return item;
                    item = item.nextSibling;
                }
            }
        }
        return null;
    }
    public static function getsByName(name:String):HtmlCollection<HtmlDom>
    {
        return doc().getElementsByName(name);
    }
}

这里有一个技巧:FlashDevelop 对haXe自动完成支持的不完善。很多js命名空间下的类不能正确支持。比如,Page.hx文件,如果不引入js.HtmlDom等三个类(红色部分),则无法提供自动完成支持,而如果引入这三个类,则编译报错(这三个类已经在js.Dom中了)。解决方案就是使用一个haXe不认识的编译开关,比如“hack”,既能让FlashDevelop提供智能支持,又不会参与到编译过程中去。

(2)Controller.hx

Controller.hx 类主要完成map功能,代码为:

import js.Dom;

#if hack
import js.HtmlDom;
import js.Document;
import js.Event;
#end

import js.Lib;

class Controller
{
    public function new(map:String = null, node:HtmlDom = null):Void
    {
        if (map == null) return;
        var mapItems:Array<String> = map.split(';');
        var dy:Dynamic = this;
        for (i in 0 ... mapItems.length)
        {
            var itemTxt:String = mapItems[i];
            var pairs:Array<String> = itemTxt.split(':');
            if (pairs.length > 0)
            {
                var v0:String = StringTools.trim(pairs[0]);
                var v1:String = v0;
                if (pairs.length > 1)
                {
                    v1 = StringTools.trim(pairs[1]);
                }
                if (v0.length > 0 && v1.length > 0)
                {
                    untyped {
                        dy[v1] = Page.getById(v0,node);
                    }   
                }
            }
        }
    }   
}

(3)Test.hx

Test类为具体的Controller,为页面添加控制,代码为:

package orc;

import js.Dom;

#if hack
import js.HtmlDom;
import js.Document;
import js.Event;
#end

import js.Lib;

class Test extends Controller
{
    private var btnSubmit:HtmlDom;
    private var tbOutput:HtmlDom;
    public function new(map:String = null, node:HtmlDom = null):Void
    {
        super(map, node);
        var self:Test = this;
        this.btnSubmit.onclick = function(e:Event):Void
        {
            self.tbOutput.innerHTML = "测试一下";
        };
    }
}

(4)Main.hx

Main类负责调度,且提供程序的入口,代码为:

package ;

import js.Dom;

#if hack
import js.HtmlCollection;
import js.HtmlDom;
import js.Document;
import js.Event;
#end

import js.Lib;
import orc.Test;

class Main
{
    static function main()
    {
        var ctrls:HtmlCollection<HtmlDom> = Page.getControllers();
        if (ctrls != null)
        {
            var l:Int = ctrls.length;
            for (i in 0 ... l)
            {
                handle(ctrls[i]);
            }
        }
    }
    static function handle(node:HtmlDom):Void
    {
        var className:String = node.getAttribute("name");
        var map:String = node.getAttribute("map");
        var root:HtmlDom = map != null ? node.parentNode: null;
        switch(className)
        {
            case "orc.test":
                new Test(map,root);
        }
    }
}

测试结果图:

image

所得到的JS文件大小为12K,其中约6K为haXe的js库。压缩后,文件大小为 4k。如果有多个页面的话,这个js文件会变大,但因为只下载一次,对于一般的网站或应用可以接受。

这样做有什么好处呢?

(1)强类型,可以充分利用IDE的提示和完成功能;

(2)可以把代码放在公共的地方,方便代码的积累。代码抽象好了,一般的应用,只映射一下就搞定了;

(3)代码结构清晰。

随着html5的到来,前端的代码量会越来越大,越来越复杂。js太灵活了不好用。拥抱haXe吧!

 

作者: xiaotie 发表于 2011-08-26 10:06 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"