zoukankan      html  css  js  c++  java
  • 让js复用smarty模板

    让js复用smarty模板

    场景:tabview或者加载更多内容的时候,往往需要从后端获取数据,然后用js生成相应的html代码,插入到相应的位置。

    通常方法:

    1. 后端直接build相应模板,然后输出到前端。
    优点:smarty模板功能强大,能使用php语法,方便调用php中自定义的处理逻辑,只用写smarty模板
    缺点:加载时传输数据大。
    2. 前端使用js模板,用后端给的数据build。
    优点:仅传输需要的数据
    缺点:页面第一次展现时需要再次发送请求build需要的数据,对于展现的实时性和seo都不是很友好
    3:混合使用smarty和js模板
    优点:解决前两个的问题
    缺点:需要维护两套模板,开发和维护成本太大

    由来:因为上述的问题,我想是不是可以让js复用smarty的模板,这样就解决了3的问题,于是便有了以下研究

    方法:
    1. 定义smarty模板文件,如a.inc。模板内容用{%literal%}标签包裹。

        {%literal%}
            {%foreach $array as $item%}
                {%if !empty($item%) || $item@first}
                    {%$item%}
                {%else%}
                    {%$item|default: 'test'%}
                {%/if%}
            {%/foreach%}
        {%literal%}

    2. 在smarty中使用模板方法

        {%capture "template_string"%}
            {%include file="a.inc"%}
        {%/capture%}
        {%$template_string = $smarty.capture.template_string%}
    
        {%include file="string:$template_string"%}

    3. 引入js使用的模板

        <script type="text/tmpl" id="test">
            {%include file="a.inc"%}
        </script>

    4. 对js模板方法进行改造
    我改造的js模板为qwrap的简易模板,改造的关键主要是将smarty的语法方式改成js的语法方式。同时将smarty使用到的方法映射成对应的js方法实现。至于具体细节就不细说了(主要修改部分已用“ // ”注释符合进行说明),直接上改造后的源码。

    /*smarty函数和js函数进行转换*/
    var StringH = {
        encode4Html: function(s) {
            var el = document.createElement('pre');
            var text = document.createTextNode(s);
            el.appendChild(text);
            return el.innerHTML;
        },
        encode4HtmlValue: function(s) {
            return StringH.encode4Html(s).replace(/"/g, "&quot;").replace(/'/g, "&#039;");
        }
    };
    window.foreach = function(arr, callback, pThis) {
        for (var i = 0, len = arr.length; i < len; i++) {
            if (i in arr) {
                callback.call(pThis, arr[i], i, arr);
            }
        }
    };
    window.empty = function(a){ return !a; };
    String.prototype.getDefault = function(a){ return this.toString() || a; };
    String.prototype.escape = function(a){
        if (a.toLowerCase() == 'html') {
            return StringH.encode4HtmlValue(this.toString());
        }
        return this.toString();
    };
    
    var Tmpl = (function() {
        /*
        sArrName 拼接字符串的变量名。
        */
        var sArrName = "sArrCMX",
            sLeft = sArrName + '.push("';
        /*
            tag:模板标签,各属性含义:
            tagG: tag系列
            isBgn: 是开始类型的标签
            isEnd: 是结束类型的标签
            cond: 标签条件
            rlt: 标签结果
            sBgn: 开始字符串
            sEnd: 结束字符串
            trans: 对标签中的内容进行转换处理 // 修改新增
        */
        var tags = {
            'if': {
                tagG: 'if',
                isBgn: 1,
                rlt: 1,
                sBgn: '");if(',
                sEnd: '){' + sLeft
            },
            'elseif': {
                tagG: 'if',
                cond: 1,
                rlt: 1,
                sBgn: '");} else if(',
                sEnd: '){' + sLeft
            },
            'else': {
                tagG: 'if',
                cond: 1,
                rlt: 2,
                sEnd: '");}else{' + sLeft
            },
            '/if': {
                tagG: 'if',
                isEnd: 1,
                sEnd: '");}' + sLeft
            },
            'foreach': {
                tagG: 'foreach',
                isBgn: 1,
                rlt: 1,
                sBgn: '");foreach(', // 修改
                trans: function(e){
                    return e.replace(/ass*([$w]+)/, function($a, $b){
                        return ',function(' + $b + ',' + $b + '_index' + ',' + $b + '_arr';
                    }); // 修改新增
                },
                sEnd: '){' + sLeft // 修改
            },
            '/foreach': {
                tagG: 'foreach',
                isEnd: 1,
                sEnd: '")});' + sLeft
            }
        };
    
        return function(sTmpl, optsName) {
            var N = -1,
                NStat = []; /*语句堆栈;*/
            var ss = [
                [/{strip}([sS]*?){/strip}/g, function(a, b) {
                    return b.replace(/[
    ]s*}/g, " }").replace(/[
    ]s*/g, "");
                }],
                [/\/g, '\\'],
                [/"/g, '\"'],
                [/
    /g, '\r'],
                [/
    /g, '\n'], /*为js作转码.*/
                [
                    /{%[sS]*?S\%}/g, /*js里使用}时,前面要加空格。*/ // 按情况修改标签分隔符
                    function(a) {
                        a = a.substr(2, a.length-2-2);
                        for (var i = 0; i < ss2.length; i++) {a = a.replace(ss2[i][0], ss2[i][1]); }
                        var tagName = a;
                        if (/^(.w+)W/.test(tagName)) {tagName = RegExp.$1; }
                        var tag = tags[tagName];
                        if (tag) {
                            if (tag.isBgn) {
                                var stat = NStat[++N] = {
                                    tagG: tag.tagG,
                                    rlt: tag.rlt
                                };
                            }
                            if (tag.isEnd) {
                                if (N < 0) {throw new Error("Unexpected Tag: " + a); }
                                stat = NStat[N--];
                                if (stat.tagG != tag.tagG) {throw new Error("Unmatch Tags: " + stat.tagG + "--" + tagName); }
                            } else if (!tag.isBgn) {
                                if (N < 0) {throw new Error("Unexpected Tag:" + a); }
                                stat = NStat[N];
                                if (stat.tagG != tag.tagG) {throw new Error("Unmatch Tags: " + stat.tagG + "--" + tagName); }
                                if (tag.cond && !(tag.cond & stat.rlt)) {throw new Error("Unexpected Tag: " + tagName); }
                                stat.rlt = tag.rlt;
                            }
                            var tmp = a.substr(tagName.length);
                            if(!!tag.trans){ tmp = tag.trans(tmp); } // 修改新增标签转换
    
                            for (var i = 0; i < ss3.length; i++) {tmp = tmp.replace(ss3[i][0], ss3[i][1]); } // 修改新增标签转换
    
                            return (tag.sBgn || '') + tmp + (tag.sEnd || '');
                        } else {
                            for (var i = 0; i < ss3.length; i++) {a = a.replace(ss3[i][0], ss3[i][1]); } // 修改新增标签转换
                            return '",(' + a + '),"';
                        }
                    }
                ]
            ];
            var ss2 = [
                [/\n/g, '
    '],
                [/\r/g, '
    '],
                [/\"/g, '"'],
                [/\\/g, '\'],
                [/print(/g, sArrName + '.push(']
            ];
            
            // 修改新增标签转换方法
            var ss3 = [
                [/|s*defaults*:s*([^s|]*)/, function(a,b){ return '.getDefault(' + b + ')'; }],
                [/|s*escapes*:s*([^s|]*)/, function(a,b){ return '.escape(' + b + ')'; }],
                [/([$w]+)@first/, function(a,b){ return '(' + b + '_index == 0)'; }],
                [/([$w]+)@last/, function(a,b){ return '(' + b + '_index == ' + b + '.length - 1)'; }],
                [/([$w]+)@index/, function(a,b){ return '(' + b + '_index)'; }]
            ];
            for (var i = 0; i < ss.length; i++) {
                sTmpl = sTmpl.replace(ss[i][0], ss[i][1]);
            }
            if (N >= 0) {throw new Error("Lose end Tag: " + NStat[N].tagG); }
            
            sTmpl = sTmpl.replace(/##7b/g,'{').replace(/##7d/g,'}').replace(/##23/g,'#'); /*替换特殊符号{}#*/
            sTmpl = 'var ' + sArrName + '=[];' + sLeft + sTmpl + '");return ' + sArrName + '.join("");';
            
            /*console.log('转化结果
    '+sTmpl);*/
            try{
                var fun = new Function(optsName, sTmpl);
            } catch (e){
                console.log && console.log("tmpl error");
                throw new Error("tmpl error");
            }
            return fun;
        };
    }());
    View Code

    结果:改造仅仅针对foreach,if这两个常用标签进行了转换。对变量调节器,如default进行了转换。对smarty用的php方法,如empty进行了转换。

    若需要用的更多的smarty的东西,也可以按类似思路进行添加。

    不过,使用该方式还是有不少问题。
    1. 毕竟smarty和js的语法有差异,部分语法可能很难实现转换。所以,若有很复杂的逻辑,那还是考虑使用单一的一个模板吧。但根据我个人的编码经验来看,改造后的模板还是能够应对绝大多数的情况的。

    2. 对js运行环境的污染。(7.21新增:可以考虑为foreach,以及加在prototype上的等方法添加命名空间进行解决。如smarty.foreach,相应的为模板函数里的foreach标签也添加上smarty命名空间就行了)

    3. 安全方面的问题。使用该方法也就意味着把smarty的代码暴露出来。不过个人认为模板里实现的本来就是很简单的,循环、判断、展现的功能,也无所谓暴不暴露了。

    ps:该文系临时赶工而出,有啥不对的地方欢迎指正。文章格式就等有空再调了。(话说调blog的格式貌似是我的软肋)
    ps:感谢qwrap的代码。

    ps:https://github.com/snadn/jsTemplateLikeSmarty

  • 相关阅读:
    maven 历史版本下载地址
    eclipse 热部署
    在线代码练习
    Intellij热部署插件JRebel
    模拟数据生成器
    电脑读取U盘总提示格式化
    变形金刚
    slamdunk正在做菜
    丧心病狂的计数
    小明在工作
  • 原文地址:https://www.cnblogs.com/snadn/p/3201562.html
Copyright © 2011-2022 走看看