由于各种原因,被逼使用前台模板。看了一下其他JS模板库的实现,发现其原理并不难,遂决定重造轮子。使用ROR.erb风格。换言之,逻辑是写在"<%"与"%>"之间,如果是注释,则用"<%#"与"%>",凡是后台传过来的变量都加上@来标记。现在这个模板系统还没有搭载任何helper方法,也不依赖任何代码,因此非常精短。
<!doctype html> <html> <head> <title>postMessage by 司徒正美</title> <meta charset="utf-8"/> <meta content="IE=8" http-equiv="X-UA-Compatible"/> <meta name="keywords" content="javascript模板 by 司徒正美" /> <meta name="description" content="javascript模板 by 司徒正美" /> <title>javascript模板 by 司徒正美</title> </head> <body> <h1>javascript模板 by 司徒正美</h1> <textarea id="tmpl" style="display:none;" > <h2><%= @name %></h2> <%# 这是注释 %> <ul> <% for(var i=0; i<@supplies.length; i++) {%> <li><%= @supplies[i] %></li> <% } %> </ul> <p style="text-indent:2em;"><%=@address %></p> </textarea> <script src="http://files.cnblogs.com/rubylouvre/tmpl.js"></script> <script> window.onload = function(){ dom.erb("tmpl", {name:"司徒正美",supplies:["第一个LI元素","第二个LI元素","第三个LI元素","第四个LI元素"],address:"异次元"}) } </script> </body> </html>
模板系统:
//司徒正美 javascript template - http://www.cnblogs.com/rubylouvre/ - MIT Licensed
(function () {
var metaObject = {
'/b': '//b',
'/t': '//t',
'/n': '//n',
'/f': '//f',
'/r': '//r',
'//': ''
};
var dom = {
quote: function (str) {
str = str.replace(/[/x00-/x1f//]/g, function (chr) {
var special = metaObject[chr];
return special ? special : '//u' + ('0000' + chr.charCodeAt(0).toString(16)).slice(-4)
});
return '"' + str.replace(/"/g, '//"') + '"';
}
};
//selector ,必须,String, 模板容器textarea的ID值
//json,必须,对象字面量或JSON
//onsite,可选,Boolean,是否就地替换掉模板容器,默认true,如果为false,则返回一个文档碎片,交由用户自己插入到需要的地方
dom.erb = function (selector, json, onsite) {
onsite = !!onsite|| true;
var el = document.getElementById(selector);
if (!el) throw "找不到目标元素";
var text = el.value,
erb = dom.erb;
if (!erb[selector]) {
var arr = text.split(//s*<%/s*|/s*%>/s*/g),
buff = ["var __views = [];/n"]
for (var i = 0, segment, n = arr.length; i < n; i++) {
segment = arr[i]
if (i & 1) { //偶数项,处理动态的逻辑
switch (segment.charAt(0)) {
case "#":
//处理注释
buff.push(erb.startOfHTML, dom.quote("<!--" + segment + "-->"), erb.endOfHTML);
break;
case "=":
if (segment.indexOf("@") !== -1) { //处理后台返回的变量(输出到页面的)
buff.push(erb.startOfHTML, segment.slice(1).replace(erb.rAt, "$1json."), erb.endOfHTML);
} else {
buff.push(erb.startOfHTML, segment.slice(1), erb.endOfHTML)
}
break;
default:
if (segment.indexOf("@") !== -1) { //处理后台返回的变量
buff.push(segment.replace(erb.rAt, "$1json."), "/n");
} else {
buff.push(segment, "/n")
}
break;
}
} else { //奇数项,处理静态的HTML片断
buff.push(erb.startOfHTML, dom.quote(segment), erb.endOfHTML);
}
}
erb[selector] = new Function("json", buff.join("") + ';return __views.join("");')
}
var div = erb.parser;
div.innerHTML = erb[selector](json);
var fragment = document.createDocumentFragment();
while (div.firstChild) {
fragment.appendChild(div.firstChild)
}
return onsite ? el.parentNode.replaceChild(fragment, el) : fragment;
}
dom.erb.parser = document.createElement("div");
dom.erb.startOfHTML = "/t__views.push(";
dom.erb.endOfHTML = ");/n";
dom.erb.rAt = /(^|[^/w/u00c0-/uFFFF_])(@)(?=/w)/g;
window.dom = dom;
})();
PS:发现javascript模板没有想象中的糟糕,尤其是大流量的页面在无法使用UI库的情况下,这是个不错的选择,例子如qq zone与ニコニコ動画。