js dom 模板渲染函数
适用页面嵌入 dom 业务,比如一个问卷弹窗等,类似 template.js、ejs 语法,不依赖 jq,zepto,vue,可直接插入 dom 中
适用页面嵌入 dom 业务,比如一个问卷弹窗等,类似 template.js、ejs 语法,不依赖 jq,zepto,vue,可直接插入 dom 中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>js模板渲染函数</title>
</head>
<body>
<script>
// js模板渲染函数
var _tpl = {
/**
* 转成可执行的js语句字符串
* @param {string} content 模板
* @param {any} data 数据
* @returns 渲染后的字符串
*/
render(content, data) {
data = data || {};
var list = ['var tpl = "";'];
/*第二步*/
var codeArr = this.transform(content); // 代码分割项数组
/*第四步*/
for (var i = 0, len = codeArr.length; i < len; i++) {
var item = codeArr[i]; // 当前分割项
if (item.type == 1) {
// js逻辑
list.push(item.txt);
} else if (item.type == 2) {
// js占位
var txt = "tpl+=" + item.txt + ";";
list.push(txt);
} else {
//文本 ,把文本中的",转义一下
var txt = 'tpl+="' + item.txt.replace(/"/g, '\\"') + '";';
list.push(txt);
}
}
list.push("return tpl;");
// list结构
// var tpl = "";
// tpl += "\n ";
// for (var i = 0; i < data.length; i++) {
// tpl += "\n ";
// if (data[i].name !== "egg") {
// tpl += "\n <h1>hello-->";
// tpl += data[i].name;
// tpl += "</h1>\n ";
// }
// tpl += "\n ";
// }
// tpl += "\n ";
// return tpl;
return new Function("data", list.join("\n"))(data);
// 最后转成dom
// <h1>hello-->nodejs</h1>
// <h1>hello-->koa</h1>
},
/**
* 从原始模板中提取 文本/js 部分
* @param {string} content
* @returns {Array<{type:number,txt:string}>}
*/
transform(content) {
// <%for(var i =0;i<data.length;i++){%><%if(data[i].name!=='egg'){%><h1><%=data[i].name%></h1><%}%><%}%>
// 匹配出来的(看fe助手)
var arr = []; //返回的数组,用于保存匹配结果
// \s 匹配任意空白符
// \S 匹配任意空白符之外的任意字符
// ? 匹配前面的子表达式0次或一次,
// * 匹配前面的子表达式0次或多次,
// []字符集合,匹配所包含的任意一个字符
// . 是任意字符 可以匹配任何单个字符 , 比如,正则表达式r.t 可以匹配这些字符串:rat、rut、r t,但是不匹配root
// .*? 表示匹配任意字符到下一个符合条件的字符,比如,正则表达式a.*?xxx 可以匹配 abxxx axxxxx abbbbb
// .* 表示任何字符的0个或多个,具有贪婪性质,直到不能匹配为止
// .? 表示任何字符的0个或1个
// .* 会根据以后的正则表达式,进行回溯,也就是从头在匹配一遍,.*?,一个匹配后,继续往下,不会进行回溯,有最小匹配的性质,因为?是非贪婪匹配,
// var reg = /<%([\s\S]*?)%>/g; //用于匹配js代码的正则
var reg = /<%(.*?)%>/g;
var match; //当前匹配到的match
var nowIndex = 0; //当前匹配到的索引
// match[1]是表达式或者占位符 for(var i =0;i<data.length;i++){ 或者 }
// match不为null,为null就是文本
// exec:方法在一个指定字符串中执行一个搜索匹配
while ((match = reg.exec(content))) {
// 保存当前匹配项之前的普通文本/占位
this.appendTxt(arr, content.substring(nowIndex, match.index));
//保存当前匹配项
var item = {
type: 1, // 类型 1 js逻辑 2:js 占位 null : 文本
txt: match[1] // 内容
};
if (match[1].substr(0, 1) == "=") {
// 如果是js占位
item.type = 2;
item.txt = item.txt.substr(1);
}
arr.push(item);
//更新当前匹配索引
nowIndex = match.index + match[0].length;
}
// arr结构
// 0: {txt: "\n "}
// 1: {type: 1, txt: "for(var i =0;i<data.length;i++){"}
// 2: {txt: "\n "}
// 3: {type: 1, txt: "if(data[i].name!=="egg"){"}
// 4: {txt: "\n <h1>hello-->"}
// 5: {type: 2, txt: "data[i].name"}
// 6: {txt: "</h1>\n "}
// 7: {type: 1, txt: "}"}
// 8: {txt: "\n "}
// 9: {type: 1, txt: "}"}
// 10: {txt: "\n "}
// 上面的代码,就是把表达式匹配出来,把文本匹配出来,用type字段表示类型
//保存文本尾部,意思就是最后一个表达式结尾如果有文字,也要匹配进来
this.appendTxt(arr, content.substr(nowIndex));
return arr;
},
/**
* 普通文本添加到数组,对换行部分进行转义
* @param {Array<{type:number,txt:string}>} list
* @param {string} content
*/
appendTxt(list, content) {
/*第三步*/
// 把回车和换行都替换成换行
content = content.replace(/\r?\n/g, "\\n");
// 这些都是文本
list.push({ txt: content });
}
};
/*第一步*/
var data = [{ name: "nodejs" }, { name: "koa" }, { name: "egg" }];
var content = `
<%for(var i =0;i<data.length;i++){%>
<%if(data[i].name!=="egg"){%>
<h1>hello--><%=data[i].name%></h1>
<%}%>
<%}%>
`;
var result = _tpl.render(content, data);
var $wrap = document.createElement("div");
$wrap.className = "container";
$wrap.innerHTML = result;
document.getElementsByTagName("body")[0].appendChild($wrap);
</script>
</body>
</html>
即可发布评论!
暂无评论
确认评论邮箱
邀请制评论