最近编写了一个简单的js模板引擎(my_template),模板字符串替换(tpl_data_replace),对象或数组转换成元素节点(OA2Element) 的方法。
代码如下:
/**
* 判断是否是数组
* @param argument
* @param {number|boolean} opt 是否用严格模式
* @return {*}
*/
function is_array(argument, opt) {
if (opt === 1 || opt === true) {
return Object.prototype.toString.call(argument) === "[object Array]";
}
return argument && (typeof argument === "object") && ("length" in argument) && (typeof argument.length === "number");
}
/**
* 判断是否是对象
* @param argument
* @return {*}
*/
function is_object(argument) {
if (argument && (typeof argument === "object") && ("length" in argument)) {
return Object.prototype.toString.call(argument) === "[object Object]";
}
return argument && (typeof argument === "object") && !("length" in argument);
}
/**
* 判断是否是函数
* @param argument
* @return {*|boolean}
*/
function is_function(argument) {
return typeof argument === "function";
}
/**
* 判断是否是字符串
* @param argument
* @return {*|boolean}
*/
function is_string(argument) {
return typeof argument === "string";
}
/**
* 判断是否是布尔值
* @param argument
* @return {boolean}
*/
function is_boolean(argument) {
return typeof argument === "boolean";
}
/**
* 判断是否是数字
* @param argument
* @return {boolean}
*/
function is_number(argument) {
return typeof argument === "number";
}
/**
* 判断是否是未定义
* @param argument
* @return {boolean}
*/
function is_undefined(argument) {
return typeof argument === "undefined";
}
/**
* 判断是否是空值
* @param argument
* @return {boolean}
*/
function is_null(argument) {
return argument === null || argument === "";
}
/**
* 是否存在[true存在,false不存在]
* @param argument
* @param {array} opt 这里面的值是代表“不存在”
* @return {boolean}
*/
function is_exist(argument, opt) {
opt || (opt = ["", null, false, undefined]);
for (var k in opt) {
if (opt[k] === argument) {
return false;
}
}
return true;
}
/**
* 判断是否是整数
* @param vars
* @return {boolean}
*/
function is_int(vars) {
return /^[-]{0,1}\d+$/i.test(vars + "");
}
/**
* 判断是否是小数
* @param vars
* @return {boolean}
*/
function is_float(vars) {
return /^[-]{0,1}[0-9]+\.{0,1}[0-9]*$/i.test(vars + "")
}
/**
* 是否是html标签元素
* @param argument
* @returns {boolean}
*/
function is_element(argument) {
var reg = /^(\[object HTML[A-Za-z\-_\.]*Element\])$/i;
return reg.test(Object.prototype.toString.call(argument));
}
/**
* 是否是 symbol 类型
* @param argument
* @return {boolean}
*/
function is_symbol(argument) {
return typeof argument === "symbol";
}
/**
* 是否是字符串(扩大范围)
* @param {string|number} str
* @returns {boolean|(*|boolean)}
*/
function y_string(str) {
return is_exist(str) && (is_string(str) || is_number(str));
}
/**
* 搜索数组中是否存在指定的值
* @param {string} needle
* @param {array} haystack
* @param {boolean} argStrict
* @return {boolean}
*/
function in_array(needle, haystack, argStrict) {
var key = "", strict = !!argStrict;
if (strict) {
for (key in haystack) {
if (haystack[key] === needle) {
return true
}
}
} else {
for (key in haystack) {
if (haystack[key] == needle) {
return true
}
}
}
return false
}
/**
* 遍历数组或对象(Object原型链上)
* @param {function} fn 传入两个参数:key,value。返回值为0或false时停止循环遍历
*/
Object.prototype.foreach = function (fn) {
if (typeof fn === "function") {
var rv;
if (is_array(this, 1)) {
for (var i = 0, len = this.length; i < len; i++) {
rv = fn.call(this, i, this[i]);
if (rv === 0 || rv === false) {
break;
}
}
}
if (is_object(this) || is_function(this)) {
for (var attr in this) {
rv = fn.call(this, attr, this[attr]);
if (rv === 0 || rv === false) {
break;
}
}
}
}
};
/**
* 遍历对象属性(对象,数组,函数)
* @param {function} fn
*/
Object.prototype.foreachAll = function (fn) {
if (typeof fn === "function") {
var list = ["[object Storage]", "[object Window]", "[object Function]"], rv;
if (is_object(this) || is_array(this, 1) || is_function(this) || in_array(Object.prototype.toString.call(this), list, true)) {
for (var attr in this) {
rv = fn.call(this, attr, this[attr]);
if (rv === 0 || rv === false) {
break;
}
}
}
}
};
/**
* 设置对象属性
* @param {object|array} obj
* @param {object} data
*/
function set_obj_attr(obj, data) {
if ((is_object(obj) || is_array(obj)) && is_object(data)) {
for (var attr in data) {
is_object(data[attr]) ? set_obj_attr(obj[attr] = is_object(obj[attr]) || is_array(obj[attr]) ? obj[attr] : {}, data[attr]) : obj[attr] = data[attr];
}
}
}
/**
* 对象或数组转换成元素节点
* @param {object|array|string} ele_data
* @param {Element|object|array} container_ele
* @returns {*}
* @constructor
*/
function OA2Element(ele_data, container_ele) {
if (is_object(ele_data)) {
var tagName = ele_data.tagName, attr = ele_data.attr, child = ele_data.child;
if (y_string(tagName)) {
var E = document.createElement(tagName), k;
for (k in ele_data) {
if (ele_data.hasOwnProperty(k)) {
E[k] = ele_data[k];
}
}
if (is_object(attr)) {
for (var k1 in attr) {
if (attr.hasOwnProperty(k1) && y_string(attr[k1])) {
E.setAttribute(k1, attr[k1]);
}
}
} else if (is_array(attr)) {
attr.foreach(function (k, v) {
if (is_object(v) && y_string(v.name) && y_string(v.value)) {
E.setAttribute(v.name, v.value);
} else if (is_array(v) && y_string(v[0]) && y_string(v[1])) {
E.setAttribute(v[0], v[1]);
}
});
}
if (is_array(child)) {
var Fragment = document.createDocumentFragment();
child.foreach(function (index, v) {
Fragment.appendChild(OA2Element(v));
//Fragment.insertBefore()
});
E.appendChild(Fragment);
}
is_element(container_ele) && container_ele.appendChild(E);
return E;
}
} else if (is_array(ele_data, 1)) {
var Fragment = document.createDocumentFragment();
ele_data.foreach(function (k, v) {
Fragment.appendChild(OA2Element(v));
});
is_element(container_ele) && container_ele.appendChild(Fragment);
return Fragment;
} else {
if (y_string(ele_data)) {
var oE = {tagName: ele_data}, v3;
for (var i = 1, len = arguments.length - 1; i < len; i++) {
v3 = arguments[i];
if (is_object(v3)) {
set_obj_attr(oE, v3);
} else if (is_array(v3)) {
y_string(v3[0]) && (oE[v3[0]] = v3[1]);
}
}
return OA2Element(oE, arguments[len]);
}
}
return false;
}
/**
* 模板字符串替换
* @param {object|array} data
* @param {string} tpl_string
* @param {string|RegExp} mode_start
* @param {string|RegExp} mode_end
* @returns {*}
*/
function tpl_data_replace(data, tpl_string, mode_start, mode_end) {
var escape=function(str){return str.replace(/([{}\[\]().+*?^$|\/])/g,"\\$1");}, type=Object.prototype.toString, rvFn=function(rv){is_array(rv)&&(rv="Array");is_object(rv)&&(rv="Object");is_function(rv)&&(rv="Function");return rv;};
var get_obj_val = function (obj, str) {
if (is_object(obj)) {
if (is_array(str)) {
for (var i = 0, len = str.length, attr, end_len = len - 1, rv; i < len; i++) {
attr = str[i];
rv = obj[attr];
if (is_object(rv) || is_array(rv, 1)) {
obj = obj[attr];
if (i === end_len) {
return rvFn(rv);
}
} else {
return rvFn(rv);
}
}
} else if (y_string(str)) {
str = str.replace(/(^[.]+|[.]+$)/g, "");
var list = str.split(/[.]+/);
return get_obj_val(obj, list);
}
}
return false;
};
var is_reg = "[object RegExp]", is_reg_mode_start = (type.call(mode_start) === is_reg), is_reg_mode_end = (type.call(mode_end) === is_reg);
if (is_reg_mode_start || mode_start === 0) {
(mode_start === 0) && (mode_start = /(@[A-Za-z0-9._\-]+@)/g);
if (is_object(data) && y_string(tpl_string)) {
is_reg_mode_end || (mode_end = /([A-Za-z0-9._\-])+/g);
return tpl_string.replace(mode_start, function (a) {
var _a = a.match(mode_end);
return get_obj_val(data, _a[0]);
});
}
} else {
y_string(mode_start) || (mode_start = "@");
y_string(mode_end) || (mode_end = "@");
if (is_object(data)) {
var k, v, r_arr = [];
for (k in data) {
v = data[k];
if (data.hasOwnProperty(k)) {
r_arr.push(escape(mode_start + k + mode_end));
}
}
if (y_string(tpl_string)) {
var r_str = r_arr.join("|");
var reg3 = new RegExp("(" + r_str + ")", "g");
return tpl_string.replace(reg3, function (a) {
var _a = a.replace(new RegExp("(^" + escape(mode_start) + "|" + escape(mode_end) + "$)", "g"), "");
return rvFn(data[_a]);
});
}
}
}
return false;
}
/**
* 简单的模板引擎
* @param {string|Element} tpl
* @param {string} start 开始标记
* @param {string} end 结束标记
* @returns {Function}
*/
function my_template(tpl, start, end) {
is_element(tpl) && (tpl = tpl.innerHTML);
var match, rv, fn, str = '';
var reg = {
start: y_string(start) ? start : "<@",
end: y_string(end) ? end : "@>",
re: {
start: /(\{.*?\})/g,
end: /([A-Za-z0-9._\-]+)/g
},
};
var regStr = {
jsCode: tpl_data_replace(reg, "({@start}.*?{@end})", reg.re.start, reg.re.end),
getJsCode: tpl_data_replace(reg, "(^\s*{@start}[=]*|{@end}\s*$)", reg.re.start, reg.re.end),
};
//构成函数里面的代码
var fnCode = ['var args = arguments;\nvar r=[];\n'],
jsCodeReg = new RegExp(regStr.jsCode, "g");
//辅助添加代码片段
var add = function (str, opt) {
opt || (opt = 1);
var _str = str;
str = str.replace(/(\n)/g, "☗n☖").replace(/(\r)/g, "☗r☖").replace(/(\t)/g, "☗t☖").replace(/(['"])/g, "\\$1");
(opt === 1) && fnCode.push("r.push('" + str + "');\n ");
(opt === 2) && fnCode.push("r.push(" + _str + ");\n ");
};
//用于清除条两边标签,获取纯的js代码片段
var reg_trim_tag = new RegExp(regStr.getJsCode, "g");
//用于清除js代码片段部分
var reg_del_js = new RegExp(jsCodeReg.source, "g");
//用于存储上一次匹配到的位置
var ma = {index: 0};
while (match = jsCodeReg.exec(tpl)) {
str = tpl.slice(ma.index, ma.index = match.index);
str = str.replace(reg_del_js, "");
add(str);
if (String(match[1]).search(reg.start + "=") !== -1) {
add(match[1].replace(reg_trim_tag, ""), 2);
} else {
fnCode.push(String(match[1]).replace(reg_trim_tag, "") + "\n ");
}
//console.log(str, match);
}
str = tpl.slice(ma.index, tpl.length);
str = str.replace(new RegExp(jsCodeReg.source, "g"), "");
add(str);
fnCode.push("return r.join('').replace(/(☗n☖)/g,'\\n').replace(/(☗r☖)/g,'\\r').replace(/(☗t☖)/g,'\\t');");
rv = fnCode.join('');
fn = new Function("data", "arg2", "arg3", rv);
//console.log(fn);
return fn;
}
测试代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="Keywords" content="关键词1,关键词2"><!--关键词-->
<meta name="Description" content="描述"><!--描述-->
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximun-scale=1.0"><!--宽度为设备宽度,初始缩放为1.0倍,最小缩放为1.0倍,最大缩放为1.0倍-->
<!-- 设置浏览器不缓存 begin -->
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-control" content="no-cache">
<meta http-equiv="Cache" content="no-cache">
<!-- 设置浏览器不缓存 end -->
<title>js模板</title>
<script type="text/javascript" src="/js/jq.min.js"></script>
</head>
<body>
<script type="text/javascript" src="/js/jquery.easing.1.3.min.js"></script>
<script type="text/javascript">
$(function () {
window.fn2233 = my_template($("#tpl-01").html());
window.fn4455 = my_template($("#tpl-02").html(), "<@js", "@>");
window.fn6677 = my_template($("#tpl-03").html(), "<%", "%>");
});
</script>
<script type="text/plain" id="tpl-01">
<div>
<p class="x111">我的模板引擎</p>
<@ var list = data.list; for(var i=0,len=list.length;i<len;i++){ @> @>
<p><@= list[i] @></p>
<@ } @>
</div>
</script>
<script type="text/plain" id="tpl-02">
<div>
<p class="x111">我的模板引擎-<@js=data.title@></p>
<@js var list = data.list; for(var i=0,len=list.length;i<len;i++){ @>
<p><@js= list[i] @></p>
<@js } @>
</div>
</script>
<script type="text/plain" id="tpl-03">
<div>
<p class="x111">我的模板引擎-<%=data.title%></p>
<% var list = data.list; for(var i=0,len=list.length;i<len;i++){ %>
<p><%= list[i] %></p>
<% } %>
</div>
<div>
<p class="x222">我的模板引擎-<%=data.title%></p>
<% var list = data.list; for(var i=0,len=list.length;i<len;i++){ %>
<p><%= list[i] %></p>
<% } %>
</div>
<% console.log(args,data); %>
</script>
</body>
</html>
如图:
一个简单的js模板引擎就弄好了。