模板
在后端语言中,都会有对应的模板语言支持
smarty,blade // PHP velocity,freemarker // jsp
然而在前端,想使用模板,就得自己动手实现啦。这时候,我们就得请出 String.replace
方法了。
思路
- 构造正则表达式
- 定义匹配的规则
- 替换数据
雏形
var str = '<div class="{{classname}}"></div>'; var pattern = /\{\{(\w+)?\}\}/g;
str.replace(pattern,function(){ return 'abc' }) //'<div class="abc"></div>'
正则表达式的构造,常常是根据需求来决定的。我们如何定义模板字符串,将影响正则表达式的声明。
扩展实践
定义正则表达式
var RE_TPL = /\{\{(\w+)?\}\}/g
定义模板字符串
var TPL = { Alert: '<div class="win-pop {{clazz}}">' + '<div class="win-t"><div></div></div>' + '<div class="win-con">' + '<div class="win-con-in">' + '<div class="win-Box" id="xwb_dlg_ct">' + '{{contentHTML}}' + '</div>' + '</div>' + '<div class="win-con-bg"></div>' + '</div>' + '<div class="win-b"><div></div></div>' +'</div>' }
定义模板解析对象
var T = { /** * [parse 模板解析函数] * @param {[String]} tpl [模板字符串] * @param {[Object]} map [模板数据] * @return {[String]} [解析后模板字符串] */ parse: function(tpl,map) { if (!map) map = {}; tpl = tpl.replace(RE_TPL,function(m,k){ var v = map[k] if(v === undefined || v === null) return ''; return v; }) return tpl; } } T.parse(TPL.Alert,{clazz:"test",contentHTML:"<span>甜甜</span>"})
基本上,我们是实现了一个简易的 JS 模板。
需求变更
有时候,我们需要定义在模板中其他逻辑,比如判断,模板嵌套等等。
具体的规则,我们是完全可以自己定义的。
新的规则
为了区分之前的模板,这里尝试使用新的模板界定符 [?xxx?]
var RE_TPLIF = /\[\?(!?)(\w+?)\?([\S\s]*?)\?\]/g;
扩展解析函数
var T = { /** * 模板缓存 * */ TPLS: {},/** * [parse 模板解析函数] * @param {[String]} tpl [模板字符串] * @param {[Object]} map [模板数据] * @return {[String]} [解析后模板字符串] */ parse: function(tpl,map) { if (!map) map = {}; if (tpl.charAt(0) !== '<') { var temp = T.TPLS[tpl]; if (temp) tpl = temp; } tpl = tpl.replace(RE_TPLIF,s0,s1,s2){ if (s0 === '!') return !map[s1] ? s2: ''; return map[s1] === undefined ? '' : s2; }) tpl = tpl.replace(RE_TPL,k){ var v = map[k] || T.TPLS[k]; if(v === undefined || v === null) { return ''; } if (v.toString().charAt(0) == '<') { return T.parse(v,map) } if (T.TPLS[v]) { return T.parse(T.TPLS[v],map) } return v; }) return tpl; },/** * 对象扩展 * @param target * @param src * @returns {Object} */ extend: function(target,src) { target = target || {}; var i,toString = Object.prototype.toString; for (i in src) { if (src.hasOwnProperty(i)) { if (typeof src[i] === "object") { target[i] = (toString.call(src[i]) === ["object Array"]) ? [] : {}; this.extend(src[i],target[i]); } else { target[i] = src[i]; } } } return target; },/** * 初始化模板 * @param map */ set: function(map) { this.extend(this.TPLS,map) } }
var TPL = { Alert: '<div class="win-pop {{clazz}}">' + '<div class="win-t"><div></div></div>' + '<div class="win-con">' + '[?title?{{Title}}?]' + '<div class="win-con-in">' + '<div class="win-Box" id="xwb_dlg_ct">' + '{{contentHTML}}' + '</div>' + '</div>' + '<div class="win-con-bg"></div>' + '</div>' + '<div class="win-b"><div></div></div>' +'</div>',Title: '<h1>简易模板</h1>' }
初始化代码
T.set(TPL); console.log(T.TPLS) console.log(T.parse(TPL.Alert,{ clazz: "abc",contentHTML: "<span>甜甜</span>" }))
模板解释
[?title?{{Title}}?] [?title?<h1>简易模板</h1>?]
是否设置 在 T.parse
的 map
对象设置 'title' 属性?
- 是,使用默认的模板渲染
{{Title}} => T.Title
- 否,不渲染模板
总结
- 模板的规则是可以自由定制的
- 通过正则表达式的捕获分组来解析数据
资源链接
http://regexpal.com/
http://regexper.com/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Glob...