extjs之模板分析

自从第一眼看到效果绚丽的ext-grid,我便相信了神力的存在。于是接下来的一段时间我在公司广为布道,说服同事们在新的项目中引进extjs框架。还未开化的人们于是诘难我:“既然ext无所不能,那么它能否让grid像excel一样仅用鼠标拖拉就能给出某个区域的信息?”

“显然不能,显然不能。”旁观者嚷嚷。

“那么合并单元格并作为一个表单保存呢?就像一个excel模板。”

“开什么玩笑,当然不可能啦。”旁观者一脸不屑。

“那么它就不是我们所要的。”

作为一个忠实但初入门的信徒,对于上述问题无可奈何的心情让我沮丧到顶点,甚至睡梦中我都在祈祷神示。直到某天起床,我发现了一张被我口水浸湿的纸条,上面歪歪扭扭地写着几个字——“Template”。

我仿佛瞥见最初造世的那束光,如今我将此光耀再引于尔身。

 


 

extjs实现Template的代码分为两个文件——Template.js和Template-more.js,前者是水,后者是糖。首先我们来分析前一个。

 (额,不知道怎么插入代码,貌似这个编辑器没有提供这个功能?网上查了下,传说中应该有个#才对。)

先来看下怎么构造一个模板,如下:

var t = new Ext.Template([
    '<div name="{id}">',
        '<span class="{cls}">模板例子</span>',
    '</div>',
]);

构造函数:

Ext.Template = function(html){
    var me = this,//至今还是难以理解为什么要把this转化成me
     a = arguments,
     buf = [];

    if (Ext.isArray(html)) {//如果参数是数组,就简单的串成字符串
        html = html.join("");
    } else if (a.length > 1) {//如果是泛参数,类似于c#的params,其中可能包括配置对象config
     Ext.each(a, function(v) {
            if (Ext.isObject(v)) {//这里就是将配置对象属性赋予me
                Ext.apply(me, v);
            } else {
                buf.push(v);
            }
        });
        html = buf.join('');//串接除配置对象以外的字符串数组(所以说参数也不可以随便乱传,只能是 ("字符串"[,..,{config}]) 格式)
    }    
    me.html = html;
    if (me.compiled) {//这里单词会有点让人困惑,我一开始还以为判断是否已经编译过,其实是判断是否要编译,从config参数传入
        me.compile();
    }
};

构造好模板,就可以给它传一个格式化作用数据对象,如{cls:'myclass'},赋给模板的values属性。

那么啥是编译呢?这个过程是怎样的呢,我们又为什么需要这个过程?可以这么理解,在ajax兴起以前,发送给客户端的html都是在服务器端一遍遍重新生成并重新发送的,有时只是一个统计值的改动就要伤筋动骨地全部重算一遍。那ajax带来的一个附加功能就是允许服务器专注计算而不需要关心html的生成和页面生命周期的维护等已经做过无数遍的工作,并且只传输这个返回的数值,插入到指定的地方就OK了。这里模板的编译也是基于同一个理念——重复性的工作,做一遍就够了。我不知道是否有类似的设计模式,但这种设计的光芒在extjs中处处闪现。下面来看compile的实现过程。

(这里插句话,updatepanel基于微软一贯的愚民政策,却显然和ajax形似神非。)

compile : function(){
        var me = this,  sep = Ext.isGecko ? "+" : ",";//判断是否浏览器是否使用Gecko引擎(比如火狐,不清楚为什么要不同处理)

        function fn(m, name){                       
         name = "values['" + name + "']";
         return "'"+ sep + '(' + name + " == undefined ? '' : " + name + ')' + sep + "'";
        }                
        eval("this.compiled = function(values){ return " + (Ext.isGecko ? "'" : "['") +
             me.html.replace(g, '').replace(/(/r/n|/n)/g, '//n').replace(/'/g, "//'").replace(this.re, fn) +
             (Ext.isGecko ?  "';};" : "'].join('');};"));
        return me;
    }

上面的eval完全是为了更容易拼凑compiled函数体而存在,本人十分讨厌这种可读性差的产物,人笨没办法,所以我打算把开头构造的那个例子拿过来看看最后compiled是个什么东东。

作为神圣的传播者,我一眼就看出了众人心中的恐惧,没错,就是正则表达式这个恶魔,但我要告诉各位正是它构建了整个模板框架的灵魂,随着信仰越来越坚定,我们也将越来越多的受到它的考验。不过幸好这次这个级数不高,正好可以练练手。

正则表达式后面带个g表示全局搜索,放在replace里面就表示替换所有匹配,如果不带g就只能替换第一个找到的匹配。字符串里的'/'是转义字符。me.html.replace(g, '')的意思就是将单反斜杠替换为双反斜杠,第二次替换就是将换行符统一(不知道再哪里能用到),同理第三次替换就是在单引号前面加个反斜杠。好了,经过一轮替换,现在me.html变成了——还是原样……当然了,因为原先的字符串里面既没有反斜杠也没有换行符也没有单引号,哈哈。

最后的replace使出了它的绝招,第二个参数变成了一个函数。关于这个函数,失传已久的《江湖二三事》中有记载:它的参数个数视与正则表达式的匹配程度而定,第一个是匹配字符串,接下来的n个是与子表达式相匹配的子字符串,倒数第二个参数是匹配字符串在整个字符串中的位置,最后的参数就是这整个字符串(这里是me.html)。关于详细信息,篇幅关系,请自行查找资料。

这里re 值为 //{([/w-]+)/}/g,可以匹配'{cls}'等以大括号包围多个字符或'-'格式的字符串,其中'([/w-]+)'就是子表达式,'cls'就是匹配的子字符串。于是将之代入fn得到',(values["myclass"] == undefined ? '' : values["myclass"] ),'(非Gecko环境下)。当将所有的字符都替换完毕,接下来就简单了,下面是最终的结果:

this.compiled = function(values){return ['

                            <div name=" ',

                            (values["id"] == undefined ? '' : values["id"] ),

                            ' "> <span class=" ',

                            (values["cls"] == undefined ? '' : values["cls"] ),

                            ' ">模板例子</span></div>

                            '].join('');

                         };

好,我们再回头想想,编译是什么?聪明的人应该有所悟了,执行得到结果,而编译则得到执行[函数],上面的compiled就是执行函数。由于事先不知道详细的html格式,因此只能动态地构建执行函数。当然可以在检索插值的同时进行values的替换,如下:

    me.html.replace(me.re, function(m, name){
           return values[name] !== undefined ? values[name] : "";
          });
虽然可以直接返回结果,但需要每次都去replace一下。如果模板实例用的频繁,那么这种方式的性能就不咋地了。

如果你追随我到此,那么你已经洞悉了extjs的模板模式,倘若假以时日,成就不可限量……

Ext.Template类还定义了几个方法,其中关键的有个doInsert方法。该方法调用了Ext.DomHelper对象的insertHtml方法,可以在生成的html片段标签头尾的前后位置插入新的dom节点并返回。关于Ext.DomHelper我们以后会详细分析。

 

 


 

直到本篇的最后,还是有人会发出冷笑:“不过是格式化字符串而已,吹得好大口气!”话虽不错,但本篇的重点其实是作者一直强调的编译思路。下一篇我将把灵魂托付给Template-more.js文件,让它指引我具备编译诸如'<span class={cls:customizeClsFn}>{text:customizeShowText}</span>'这种支持函数的字符串的技能,然后消除一切不和谐的声音!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值