Javascript微模板引擎

1 comment , Tagged : , ,

Javascript微模板引擎,好吧,叫得这么好听其实就一简陋的前端模板。

后端模板大家应该听得比较多吧(Smarty,Velocity等),而前端我们通常是直接在JS里拼接我们需要的html,这种混合写在一起的方式,导致html结构稍微复杂点就很难看清楚,对维护造成一定的不便。所以人们就“发明”了模板这东西,使用一些特殊标记来代表数据循环、值替换等功能,使内容格式尽可能清晰。

模板的核心就是模板解析,下面要展示的引擎也仅有这个功能,没有其它扩展,要求不高的可以拿去试试!!下面我们来看一下该引擎的程序:

// Javascript Micro Templating
// pzling - http://pzling.com/
var JSMT = JSMT || {};
(function(JSMT){
	var tmplCaches = {},
		dictDecode = { "quot": '"', "lt": "<", "gt": ">", "amp": "&", "nbsp": " " };
		dictEncode = { '"': "quot", "<": "lt", ">": "gt", "&": "amp", " ": "nbsp" };
 
	/**
	 * HTML解码
	 * @param {String} html
	 */
	function htmlDecode(html) {
		return String(html).replace(/&(quot|lt|gt|amp|nbsp);/ig, function(all, key) {
			return dictDecode[key];
		});
	}
 
	/**
	 * HTML编码
	 * @param {String} html 
	 */
	function htmlEncode(html) {
		return String(html).replace(/["<>& ]/g, function(all) {
			return "&" + dictEncode[all] + ";";
		});
	}
 
	/**
	 * 模板解析函数
	 * @param {String} templating 模板字符
	 */
	function analyze(templating){
		var _temp =templating.replace(/<#([\s\S]*?)#>/g,function(s){
			return s.replace(/(\'|\\)/g,'\\$1')
					.replace(/[\r\n]/g,' ')
					.replace('<#',"_s.push('")
					.replace('#>',"');")
					.replace(/{\$([\s\S]*?)\$}/g,'\',$1,\'');
		});
		return new Function('var _s=[];' + _temp + ';return _s');
 
	}
 
	/**
	 * 模板注册函数
	 * @param {String} id 模板字符
	 * @param {String} data 模板字符
	 */
	function register(id,data){
		var tar;
		if(!id) return '';
		if(typeof id === 'object' && id.tagName){
			tar = id ;
			id = tar.getAttribute('id');
		}else{
			tar = document.getElementById(id);
		}
		if(!tmplCaches[id]){
			tmplCaches[id] = analyze(tar.innerHTML);
		}
		return tmplCaches[id].apply(data).join('');
	}
 
	/**
	 * 功能主调方法
	 * @param {String / Object} id 字符或dom对象
	 * @param {ALL} json 模板主要数据,任何数据类型都可以,模板使用this获取数据
	 */
	JSMT.render = function(id,json){
		var output = register.apply(this,[id,json || '']);
		return output;
	}
	/**
	 * 辅助方法
	 */
	JSMT.htmlEncode = htmlEncode;
	JSMT.htmlDecode = htmlDecode;
})(JSMT);

程序对外提供三个方法,render是功能的主调方法,另外是两个辅助方法,用于html的编码与解码(防XSS)。下面再来看一下模板的定义方法(我拿DEMO里的模板来讲解):

    <ul id="list">
        <script type="text/template" id="item_tmpl1">
            for(var i = 0 ,len = this.length ; i < len ; i++){
                <# 
                    <li>
                        <p><a href='{$ this[i].name $}'>{$ this[i].name $}</a></p>
                        {$ JSMT.render("item_tmpl2",this[i].detail) $}
                    </li> 
                #>
            }
        </script>
    </ul>
    <script type="text/template" id="item_tmpl2">
        <#
            <p style="margin-left:10px;">Age:{$ this.age $}</p>
            <p style="margin-left:10px;">Gender:{$ JSMT.htmlDecode(this.gender) $}</p>
            <p style="margin-left:10px;">Hobby:{$ this.hobby$}</p>
        #>
    </script>

<# #>:要输出的(html)内容;

{$ $}:要替换的变量,必须嵌套在<# #>标记内;

this:代替你传进来的数据;

PS:{$ $}只能是变量、函数调用,三元运算。

模板内容包含在<script type=”text/template”>标签内,type=”text/template”是我自定义的,因为系统识别不出这是什么type,所以它只直接忽略掉里面的内容。这里我使用了嵌套调用,在模板可以使用任意JS语法(你要定义一个函数都可以),当然一般只用 if 作逻辑处理还有 for 作循环。

接下来是功能的调用:

<script>
	var container = document.getElementById('list');
	container.innerHTML = JSMT.render(
		'item_tmpl1',
		[
			{
				name:'marry',
				detail:{
					age : 10,
					gender  : '<span style="color:red;">girl</span>',
					hobby : 'reading'
				}
			},
			{
				name:'Joe',
				detail:{
					age : 23,
					gender  : '&lt;span&nbsp;style="color:red;"&gt;girl&lt;/span&gt;',
					hobby : 'playing'
				}
			}
		]
	);
</script>

就这么简单,传入id(或dom)与数据(JSON或JSON数组,其实什么都行,看你模板里怎么写),返回解析后的内容。方法可多次调用,例如在数据翻页,传入不同数据调用。

使用方法就上面这些,功能简单,不过在一些小项目还是可以用用的。现在JQuery与EXT等的模板引擎也都很强大的,写这个简陋的功能也仅仅是学习一下,以后再扩展其它功能

最近一直没什么状态,这功能也是每天写不到5分钟,拖到昨天才整完。前两天才开始配了下nodeJS的环境,看了些资料,终于又觉得有些小兴奋有点状态了,这几天多抽时间学一下,也希望下周可以顺利点吧。

1 Responses to this entry

  1. September 17th, 2011 at 08:02
    kinogam

    从不相信没有单元测试的东西

Drop Comments

大笑 感动 酷 无语 色咪咪 雷人 晕 怒 囧 打酱油 嘿嘿 yy