模板
在后端语言中,都会有对应的模板语言支持
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,function(m,s0,s1,s2){
if (s0 === '!')
return !map[s1] ? s2: '';
return map[s1] === undefined ? '' : s2;
})
tpl = tpl.replace(RE_TPL, function(m,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...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。