javascript模板系统 ejs v3
本版本增添了局部模板功能,并且允许主模板调用局部模板,局部模块调用局部模块,并去掉onsite变量,不再提供解析成毕的文档碎片给使用者。它使用双重缓存,一是缓存那些通过同步请求得到的文本而成的数组,一是整体解析完毕得到的模板函数。模板函数是通过数组元素拼凑动成解析而成的,这是大大提高了效率。不过由于新功能的加入,虽然动用了新的构筑算法也比不上v2的构筑速度了……
有人说不要使用<%与%>做界定符,这个问题我在v1版本已经提出过了,这些都是可以自定义的。本文的例子将演示一下如何使用Django的{{与}}风格。
最后隆重推介一下本版本的新功能。不过由于条件有限,无法演示。现在模板不单单是内嵌于页面的script标签之内,也可以放置在一个独立的文件之内,如html,ejs,text,随你起什么后缀名。这个文件将会用同步请求回来用于构筑模板函数。我们可以用使用url属性,或在模板中使用<%: /template/partail.ejs >实现。一般而言,url是用于主模板,而<%: url %>是用于局部模板。如果我们在配置对象中同时使用selector与url,selector的优先级是高于url的。
var data = dom.ejs({ selector: "tmpl" , url: "/template/aaa.html" , left: "{{" , right: "}}" , json: { name: "司徒正美" , blog: "ruby louvre" address: "异次元" } }); |
源码:
//司徒正美 javascript template - http://www.cnblogs.com/rubylouvre/ - MIT Licensed ( function () { if (!String.prototype.trim){ String.prototype.trim = function (){ return this .replace(/^[\s\xa0]+|[\s\xa0]+$/g, '' ); } } 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, '\\"' ) + '"' ; } }, metaObject = { '\b' : '\\b' , '\t' : '\\t' , '\n' : '\\n' , '\f' : '\\f' , '\r' : '\\r' , '\\' : '\\\\' }, startOfHTML = "\t__views.push(" , endOfHTML = ");\n" ; ( function (){ //http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx var s = [ "XMLHttpRequest" , "ActiveXObject('Msxml2.XMLHTTP.6.0')" , "ActiveXObject('Msxml2.XMLHTTP.3.0')" , "ActiveXObject('Msxml2.XMLHTTP')" , "ActiveXObject('Microsoft.XMLHTTP')" ]; if ( eval( "''+/*@cc_on" + " @_jscript_version@*/-0" )*1 === 5.7 && location.protocol === "file:" ){ s.shift(); } for ( var i = 0 ,el;el=s[i++];){ try { if (eval( "new " +el)){ dom.xhr = new Function( "return new " +el) break ; } } catch (e){} } })(); dom.partial = function (url){ var xhr = dom.xhr(); xhr.open( "GET" ,url, false ); xhr.setRequestHeader( "If-Modified-Since" , "0" ); xhr.send( null ); return xhr.responseText|| "" } dom.tmpl = function (str,rLeft,rRight,sRight){ var arr = str.trim().split(rLeft),self = arguments.callee,buff = [],url,els,el,i = 0, n= arr.length; while (i<n) { els = arr[i++]; el = els.split(rRight); if (els.indexOf(sRight) !== -1){ //这里不使用els.length === 2是为了避开IE的split bug switch (el[0].charAt(0)) { case "#" : //处理注释 break ; case "=" : //处理后台返回的变量(输出到页面的); buff.push(startOfHTML, el[0].substring(1), endOfHTML) break ; case ":" : //处理局部模板 url = el[0].substring(1).trim(); //缓存构筑函数的数组 self[url] = self[url] || self.call( null ,dom.partial(url),rLeft,rRight,sRight); buff = buff.concat(dom.tmpl[url] ); break ; default : buff.push(el[0], "\n" ); }; el[1] && buff.push(startOfHTML, dom.quote.call( null ,el[1]), endOfHTML); } else { buff.push(startOfHTML, dom.quote.call( null ,el[0]), endOfHTML); } } return buff; } dom.ejs = function (obj) { var sLeft = obj.left || "%>" , sRight = obj.right || "<%" , rLeft = new RegExp( "\\s*" +sLeft+ "\\s*" ), rRight = new RegExp( "\\s*" +sRight+ "\\s*" ), buff = [ "var __views = [];\n" ], key = obj.selector || obj.url,str; if (obj.selector){ var el = document.getElementById(key); if (!el) throw "找不到目标元素" ; str = el.text; } else { str = dom.partial(key); if (!str) throw "目标文件不存在" ; } if (!dom.tmpl[key]){ //缓存模板函数 buff = buff.concat(dom.tmpl.call( null ,str,rLeft,rRight,sRight)); dom.tmpl[key] = new Function( "json" , "with(json){" +buff.join( "" ) + '\t};return __views.join("");' ); } return dom.tmpl[key](obj.json || {}); }; window.dom = dom; })(); |
示例:
<! doctype html> < html > < head > < 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 > < div id="tmplTC">这是容器</ div > < script id="tmpl" type="tmpl"> < h2 >{{= name }}{{= name }}</ h2 > {{# 这是注释!!!!!!!!! }} < ul > {{ for(var i=0; i< uls.length ; i++){ }} <li>{{= uls[i] }}的名字是{{= name }}</ li > {{ } }} </ ul > {{ var color = "color:red;" }} < p style="text-indent:2em;{{= color }} ">{{= address }}</ p > </ script > < script src="dom/ejs.js"></ script > < script > window.onload = function(){ var els = []; for(var i=0;i< 1000 ;i++){ els.push("第"+i+"个元素") } var a = new Date var data = dom.ejs({ selector:"tmpl", left:"{{", right:"}}", json: { name:"司徒正美", uls:els, address:"异次元" } }); document.getElementById("tmplTC").innerHTML = data; alert( new Date-a) } </script> </ body > </ html > |
现在我有一个考量,就是随着模板规模的膨胀,里面可能夹杂着越来越多变量,我们就很难分辨得清那些后台传过的东西,那些是本地的临时变量,后台的需求一变更,后台的json数据也就要改动。这维护起来非常困难。因此我非常欣赏ruby的变量书写风格,从变量名就知它是实例变量,类变量,普通变量与常量,它还有符号这东西呢,我会v4版本加入重新加入@标识符的。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步