前端模板之ace-template 循環列表(一)


很早之前就接觸過javascript的模板引擎,今天看了這篇文章后深受啟發:初識前端模板 ,作者對目前流行的前端引擎作了深入的對比。基於性能和擴展性,兼容性的結果,我決定先對ace-temlate這個模板引擎進行一下演練。

先看一下前面作者的分析比較圖:

image

image

先看下AceTemplate的源碼:

var AceTemplate = AceTemplate || {};
 
(function(){
    /**
     * Ace Engine Template
     * 一套基於HTML和JS語法自由穿插的模板系統
     * http://code.google.com/p/ace-engine/wiki/AceTemplate
     * @author 王集鵠(wangjihu,http://weibo.com/zswang) 魯亞然(luyaran,http://weibo.com/zinkey)
     * @version 2011-07-06 
      * @copyright (c) 2011, Baidu Inc, All rights reserved.
     */
 
    /* Debug Start */
    var logger = {
        /**
         * 打印日志
         * @param {Object} text 日志文本
         */
        log: function(text) {
            /*
            var dom = document.getElementById("log");
            if (dom) {
                dom.value += text + "\n";
            }
            */
            window.console && console.log(text)
        }
    };
    /* Debug End */
    
    var htmlDecodeDict = { "quot": '"', "lt": "<", "gt": ">", "amp": "&", "nbsp": " " };
    var htmlEncodeDict = { '"': "quot", "<": "lt", ">": "gt", "&": "amp", " ": "nbsp" };
    var lib = {
        /**
         * 通過id獲得DOM對象
         * @param {String} id
         */
        g: function(id){
            if (typeof id != "string") 
                return id;
            return document.getElementById(id);
        },
        /**
         * HTML解碼
         * @param {String} html
         */
        decodeHTML: function(html) {
            return String(html).replace(/&(quot|lt|gt|amp|nbsp);/ig, function(all, key) {
                return htmlDecodeDict[key];
            }).replace(/&#u([a-f\d]{4});/ig, function(all, hex) {
                return String.fromCharCode(parseInt("0x" + hex));
            }).replace(/&#(\d+);/ig, function(all, number) {
                return String.fromCharCode(+number);
            });
        },
        
        /**
         * HTML編碼
         * @param {String} html 
         */
        encodeHTML: function(html) {
            return String(html).replace(/["<>& ]/g, function(all) {
                return "&" + htmlEncodeDict[all] + ";";
            });
        },
        /**
         * 獲得元素文本
         * @param {Element} element
         */
        elementText: function(element) {
            if (!element || !element.tagName) return "";
            if (/^(input|textarea)$/i.test(element.tagName))
                return element.value;
            return lib.decodeHTML(element.innerHTML);
        }
    };
    
    /**
     * 解析器緩存
     */
    var readerCaches = {};
    
    /**
     * 是否注冊了所有模板
     */
    var registerAll = false;
 
    /**
     * 構造模板的處理函數
     * 不是JS塊的規則
     *     非主流字符開頭
     *         示例:漢字、#{value}、<div>
     *         正則:/^\s*[<>!#^&\u0000-\u0008\u007F-\uffff].*$/mg
     *     html標記結束,如:
     *         示例:>、src="1.gif" />
     *         正則:/^.*[<>]\s*$/mg
     *     沒有“雙引號、單引號、分號、逗號,大小括號”,不是else等單行語句、如:
     *         示例:hello world
     *         正則:/^(?!\s*(else|do|try|finally)\s*$)[^'":;,\[\]{}()\n\/]+$/mg
     *     屬性表達式
     *         示例:a="12" b="45"、a='ab' b="cd"
     *         正則:/^(\s*(([\w-]+\s*=\s*"[^"]*")|([\w-]+\s*=\s*'[^']*')))+\s*$/mg
     *     樣式表達式
     *         示例:div.focus{color: #fff;}、#btnAdd span{}
     *         正則:/^\s*([.#][\w-.]+(:\w+)?(\s*|,))*(?!(else|do|while|try|return)\b)[.#]?[\w-.*]+(:\w+)?\s*\{.*$/mg
     * @param {String} template 模板字符
     */
    function analyse(template) {
        var body = [], processItem = [];
        body.push("with(this){");
        body.push(template
            .replace(/[\r\n]+/g, "\n") // 去掉多余的換行,並且去掉IE中困擾人的\r
            .replace(/^\n+|\s+$/mg, "") // 去掉空行,首部空行,尾部空白
            .replace(/((^\s*[<>!#^&\u0000-\u0008\u007F-\uffff].*$|^.*[<>]\s*$|^(?!\s*(else|do|try|finally)\s*$)[^'":;,\[\]{}()\n\/]+$|^(\s*(([\w-]+\s*=\s*"[^"]*")|([\w-]+\s*=\s*'[^']*')))+\s*$|^\s*([.#][\w-.]+(:\w+)?(\s*|,))*(?!(else|do|while|try|return)\b)[.#]?[\w-.*]+(:\w+)?\s*\{.*$)\s?)+/mg, function(expression) { // 輸出原文
                expression = ['"', expression
                    .replace(/&none;/g, "") // 空字符
                    .replace(/["'\\]/g, "\\$&") // 處理轉義符
                    .replace(/\n/g, "\\n") // 處理回車轉義符
                    .replace(/(!?#)\{(.*?)\}/g, function (all, flag, template) { // 變量替換
                        template = template.replace(/\\n/g, "\n").replace(/\\([\\'"])/g, "$1"); // 還原轉義
                        var identifier = /^[a-z$][\w+$]+$/i.test(template) &&
                            !(/^(true|false|NaN|null|this)$/.test(template)); // 單純變量,加一個未定義保護
                        return ['",', 
                            identifier ? ['typeof ', template, '=="undefined"?"":'].join("") : "", 
                            (flag == "#" ? '_encode_' : ""), 
                            '(', template, '),"'].join("");
                    }), '"'].join("").replace(/^"",|,""$/g, "");
                if (expression)    
                    return ['_output_.push(', expression, ');'].join("");
                else return "";
            }));
        body.push("}");
        var result = new Function("_output_", "_encode_", "helper", body.join(""));
        /* Debug Start */
        logger.log(String(result));
        /* Debug End */
        return result;
    }
 
    /**
     * 格式化輸出
     * @param {String|Element} id 模板ID或是模板本身(非標識符將識別為模板本身)
     * @param {Object} data 格式化的數據,默認為空字符串
     * @param {Object} helper 附加數據(默認為模板對象)
     */
    AceTemplate.format = function(id, data, helper) {
        if (!id) return "";
        var reader, element;
        if (typeof id == "object" && id.tagName) { // 如果是Dom對象
            element = id;
            id = element.getAttribute("id");
        }
        helper = helper || this; // 默認附加數據
        reader = readerCaches[id]; // 優先讀取緩存
        if (!reader) { // 緩存中未出現
            if (!/[^\w-]/.test(id)) { // 合法的標識符按id讀取
                if (!element) {
                    element = lib.g(id);
                }
                reader = this.register(id, element);
            } else {
                reader = analyse(id);
            }
        }
        var output = [];
        reader.call(data || "", output, lib.encodeHTML, helper);
        return output.join("");
    };
    
    /**
     * 注冊模板,如果沒有參數則是注冊所有script標簽模板
     * @param {String} id 模板ID
     * @param {Element|String} target 模板對象或者是模板字符串,如果沒有則默認獲取id對應的DOM對象
     */
    AceTemplate.register = function(id, target) {
        if (!arguments.length && !registerAll) { // 無參數並且沒有注冊過
            registerAll = true;
            var scripts = document.getElementsByTagName("script");
            for (var i = 0; i < scripts.length; i++) {
                var script = scripts[i];
                if (/^(text\/template)$/i.test(script.getAttribute("type"))) {
                    var id = script.getAttribute("id");
                    id && arguments.callee.call(this, id, script);
                }
            }
        }
        if (!id) return;
        if (readerCaches[id]) { // 如果已經注冊
            return readerCaches[id];
        }
        if (typeof target != "string") {
            if (typeof target == "undefined") {
                target = lib.g(id);
            }
            target = lib.elementText(target);
        }
        return readerCaches[id] = analyse(target);
    };
    
    /**
     * 注銷模板
     * @param {String} id 模板ID
     */
    AceTemplate.unregister = function(id) {
        delete readerCaches[id];
    };
})();

 

1.使用Jquery輸出列表

Html模板部分

 
<script id="t1" type="text/template">
            for (var i = 0; i < this.length; i++) {
                var item = this[i];
                <li>
                    <b>第#{i + 1}名</b><a href="#{item.url}" target="blank">#{item.title}</a>
                    if (item.isnew) {
                        <span>new!</span>
                    }
                </li>
            }
    </script>
    <div>
        <ul id="t1out">            
        </ul>
    </div>

Js部分

<script type="text/javascript">
        $(function () {
            var movieList = [
                    {
                        title: "建黨偉業",
                        url: "#"
                    },
                    {
                        title: "變形金剛3",
                        url: "#",
                        isnew: true
                    },
                    {
                        title: "功夫熊貓2",
                        url: "#"
                    },
                    {
                        title: "加勒比海盜4",
                        url: "#"
                    },
                    {
                        title: "3d肉蒲團",
                        url: "#"
                    }
                ];
            $("#t1out").html(AceTemplate.format("t1",movieList));
        });
 
    </script>

熟悉MVC的朋友是不是一眼就看出來,對,這就是JS的MVC模板引擎。

是不是很簡單,我們來總結一下循環輸出的語法規則:

1.使用Javascript原始的循環嵌套HTML代碼定義

for (var i = 0; i < this.length; i++) {
var item = this[i];
<li>
<b>第#{i + 1}名</b><a href="#{item.url}" target="blank">#{item.title}</a>
if (item.isnew) {
<span>new!</span>
}
</li>

2.使用Jquery的語法規則嵌套HTML定義

$.each(this, function(i, item) {
<li>
<b>第#{i + 1}名</b><a href="#{item.url}" target="blank">#{item.title}</a>
if (item.isnew) {
<span>new!</span>
}
</li>

 

DEMO下載:下載地址 (猛擊它就可以下載了)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM