网页模板是把动态的数据和静态的表现组装到一起的工具,使得内容与表现方式可以分离,是 Web 开发中的重要技术手段。早期的 Web 开发很简单,还没“模板 ”的概念。只需把数据提取出来,放在 HTML 里面显示就达到程序的目的。HTML代码混合于逻辑代码之中,HTML 就是直接显示的内容,内嵌在 HMTL 中<% ... %>表示为后台(服务端)执行代码,但很多情况<% ... %>中又有 HTML 的片段,至于怎么拼凑出 HTML 片段的方式方法各式各样、与多种的后台语言掺杂一起( ASP、PHP、JSP )各显神通。
$$.tpl = { fillData : function(tpl, data){ // 匹配闭合tag的正则。 var divBlock = '<{0}\\s*[^>]*>((?:(?=([^<]+))\\2|<(?!{0}\\s*[^>]*>))*?)<\/({0})>'; var matchValue = (function(){ var regexp = /{([^}\n]*)}/ig ,execJS = /^\[([^\]]*)\]$/ ,getParent = /^parent\.\b(\w*)\b$/i ,getRoot = /^root\.\b(\w*)\b$/i; /** * @private * @param {mixed} v * @return {Boolean} 是否hash(是否对象)。 */ function isObject(v){ return !!v && isObject.toString.call(v) == isObject.token; } isObject.toString = Object.prototype.toString isObject.token = '[object Object]'; /** * 替换置,如果未发现匹配的值,返回什么?一般原模版的格式({xxx}),即m1。 * 注意那些无意义的值除外。 */ function falsey(value, tpl){ switch(value){ case false: return 'false'; case '': return value; case 0: return value; } return !!value ? value : tpl; } function replace(m1, m2){ var jsCode ,parent = arguments.callee.parent ,root = arguments.callee.root; if(execJS.test(m2)){ execJS.lastIndex = 0; jsCode = execJS.exec(m2); jsCode = jsCode[1]; try{ // 写try之目的为容错性的设计。 with(this){ jsCode = eval(jsCode); } return jsCode; }catch(e){ return '空值'; } } if(isObject(parent) && getParent.test(m2)){ /* 父级寻址 */ getParent.lastIndex = 0; m2 = m2.match(getParent); m2 = m2[1]; return falsey(parent[m2], m1); }else if(isObject(root) && getRoot.test(m2)){ /* 全称寻址 */ getRoot.lastIndex = 0; m2 = m2.match(getRoot); m2 = m2[1]; return falsey(root[m2], m1); }else{ return falsey(this[m2], m1); } } /** * Lazy function * @param {String} tpl * @param {Object} dataObj * @param {Object} parentObj * @return {String} */ return function(tpl, dataObj, parentObj){ if(!replace.root){ replace.root = $$.tpl.root; } replace.parent = parentObj || 'itself'; // 无else部分,即表示全部就是trueBlock replace.scope = dataObj; return tpl.replace(regexp, replace.delegate());// set scope object! } })(); var matchIf = (function(){ var elseReg = /<else\s*\/?>/i ,evalReg = /<if\s*(?:eval="([\w\d=<>\.!'\|\(\)]*)")[^>]*>/i ,tabReg = /\t{2}/g ,ifReg = new RegExp(divBlock.format('if'), 'ig'); /** * 运算<if eval="exp">内容。 * @param {Object} data * @param {Object} parent * @param {String} ifTag * @return {Boolean} */ function evalExp(data, parent, ifTag){ var exp; evalReg.lastIndex = 0; if(!evalReg.test(ifTag)){ $$.console.error('输入的标签{0}不是一个标准的if tag'.format(ifTag)); throw '不是一个标准的if tag'; } evalReg.lastIndex = 0; exp = evalReg.exec(ifTag); exp = exp[1]; exp = '(' + exp +')'; // make it as the Expression 表达式 // debugger; // 通过with(data){}给定对象可见的范围。如果没有matchedObj,则为this。这也是有可能的。 with(data || parent || this){ try{ exp = !!eval(exp); }catch(e){ exp = false; } } return exp; } /** * @this {OBject} 值对象 */ function replace(m1, m2){ var evalResult // 运算表达式后的结果 ,trueBlock // true块 ,falseBlock = null // false块,默认没有 elseReg.lastIndex = 0; if(elseReg.test(m2)){// 有else部分 var arr = m2.split(elseReg); if(!arr || arr.length < 2){ $$.console.error('if-else不完整'); throw "if-else不完整"; }; trueBlock = arr[0]; falseBlock = arr[1]; }else{ trueBlock = m2; // 无else部分,即表示全部就是trueBlock } trueBlock = $$.trim(trueBlock); trueBlock = trueBlock.replace(tabReg, '\t');// 消除多余tab符。 // 求 if 的表达式 evalResult = evalExp(this, arguments.callee.parent, m1); if(evalResult){ return trueBlock; }else if(!evalResult && falseBlock == null){ return ''; }else if(!evalResult && falseBlock){ return falseBlock; }else{ // 不应该会走到这里的。若真的走到便抛出一个异常。 $$.console.error('求if-else块时发生致命错误!'); throw '求if-else块时发生致命错误!'; } } return function(tpl, data, parentData){ ifReg.lastIndex = 0;// 有global时需要注意 lastIndex的问题。 if(ifReg.test(tpl)){ replace.parent = parentData; replace.scope = data; return tpl.replace(ifReg, replace.delegate()); }else{ return tpl; } } })(); function replace(m1, m2, m3, m4){ var parentData = this ,values = parentData[m4] ,callee = $$.tpl.fillData ,str = '' ,arrTpl; if(values && !values.pop){ return callee(m2, values); // 递归 for sub }else if(values && values.pop){/* 有时候会因为大小写问题,无法匹配。请检查key即(m4)是否一致 */ m2 = callee(m2, values); // 递归 for sub for(var i = 0, j = values.length; i < j; i++){ value = values[i]; m2 = matchIf(m2, value, parentData); arrTpl = callee(m2, value); str += matchValue(arrTpl, value, parentData); } return str; }else if(values == false /* 特定为false的情况 */ ){ return str; }else{ $$.console.warn('No data provided');// 怎么数据源没提供数据? 宜debug之 return 'edkTpl_nothing'; } } /** * @param {String} tpl * @param {Object} data * @return {String} */ $$.tpl.fillData = function (tpl, data){ if(!$$.tpl.root){ $$.tpl.root = data; } if(data && !data.pop){ replace.scope = data; var _replace = replace.delegate(); for(var i in data){ tpl = tpl.replace(new RegExp(divBlock.format(i), 'i'), _replace); } tpl = matchIf(tpl, data, data); return matchValue(tpl, data); }else if(data && data.pop){ return tpl; }else if(!data){ // throw '没有输入数据的参数 data?'; } return ''; } return $$.tpl.fillData(tpl, data); } };这是我第一次写的模板,纯练手,因此非常粗糙。