上一篇,我们实现了简单的 each 渲染处理,以最简单的方式,但是在实际的使用中,我们的需求远远没有那么简单。
这里我们对实际需求做出对应的调整,这是一个渐进的过程,经过一次次改进才能达到最终效果。
1.解决多层嵌套问题:
要解决多层嵌套问题,主要考虑 :
a.遍历主体。 即我要遍历的数组是什么呢?在前一篇,我们直接写死 “data”,默认 data 就是数组,但是当要进行多层次嵌套是,我们是不确定接下来要遍历的主体会是什么命名,所以,笔者一开始想到的是通过代码 在 {each} 里多加一个值,作为深层次遍历的数组对象 ,像这样:{each data.value} ,这里的 data.value 就是下一层遍历的主体(数组)。
这样子 ,case:'}' 这里我们将这样处理:
case "}":
isvarcode = false;
var varvalue = varcode.join("");
if(varvalue.indexOf("each")==0)
{
var eachInfo = varvalue.split(' ');
resultcode.push("for (var index in "+eachInfo[1]+") {var value = "+eachInfo[1]+"[index];");
}
else if(varvalue==="/each")
{
resultcode.push(" }");
}else{
varvalue = varvalue.replace("$index","index").replace("$value","value");
resultcode.push(" result.push(" + varvalue + ") ;");
}
varcode=[];
break;
varvalue.split(' '); 这里按照 空格拆分,那第二个就是表示 将要遍历的主体,我们将它当作 代码内容保存即可。
这样,对应的,我们的模板改成这样:
<h3>{data.key}</h3><ul>{each data.value}<li>{$index}:{$value.name}</li>{/each}</ul>
然后处理后传入:
{key:"this is key",value:[{name:"name1"},{name:"name2"},{name:"name3"},{name:"name4"}]}
得到:
<h3>this is key</h3><ul><li>0:name1</li><li>1:name2</li><li>2:name3</li><li>3:name4</li></ul>
b.每一层 的 index 和 value 区分问题。正常我们写循环嵌套时,我们每一层的 index 值一般应该要不同,不然像 js 会有作用域提升问题,而如果是静态语言(c#/java)的话,重名根本是不允许的,所以,我们这里需要对index/value 进行不同层次要名称不同处理才行。
既然要自动处理的,那笔者便想到了用一个外部变量 eachIndex 保存顺序,然后每一层的 index 就变成 index_eachIndex,每深入一层,这个 eachIndex就要 +1,即遇到 each 指令的话,将做如下处理:
if(varvalue.indexOf("each")==0)
{
var eachInfo = varvalue.split(' ');
resultcode.push("for (var index_"+eachIndex+" in "+eachInfo[1]+") {var value_"+eachIndex+" = "+eachInfo[1]+"[index_"+eachIndex+"];");
eachIndex++;
}
那么新的问题随之而来:我怎么知道我后面拼接的变量用的是当前层的 index/value 呢?
其实这个好解决,因为每一次遇到 each 时,我们的 eachIndex 都会 +1 ,那么在当前层使用的 index 就是 index_(eachIndex-1)。所以如果在拼接的代码中遇到需要用到当层次的 index/value 时,如下处理即可:
varvalue = varvalue.replace("$index","index_"+(eachIndex-1)).replace("$value","value_"+(eachIndex-1));
resultcode.push(" result.push(" + varvalue + ") ;");
以下给出方法的代码:
function Template2code(template) {
var resultcode = [];
resultcode.push("function func(data) { var result=[];");
var tempcode = [];
var varcode = [];
var isvarcode = false;
var eachIndex = 0;
for (var key in template) {
var value = template[key];
switch (value) {
case "\"":
if (isvarcode) {
varcode.push("\\\"")
}
else {
tempcode.push("\\\"");
}
break;
case "{":
isvarcode = true;
resultcode.push(" result.push(\"" + tempcode.join("") + "\") ;");
tempcode=[];
break;
case "}":
isvarcode = false;
var varvalue = varcode.join("");
if(varvalue.indexOf("each")==0)
{
var eachInfo = varvalue.split(' ');
resultcode.push("for (var index_"+eachIndex+" in "+eachInfo[1]+") {var value_"+eachIndex+" = "+eachInfo[1]+"[index_"+eachIndex+"];");
eachIndex++;
}
else if(varvalue==="/each")
{ eachIndex--;
resultcode.push(" }");
}else{
varvalue = varvalue.replace("$index","index_"+(eachIndex-1)).replace("$value","value_"+(eachIndex-1));
resultcode.push(" result.push(" + varvalue + ") ;");
}
varcode=[];
break;
default:
if (isvarcode) {
varcode.push(value)
}
else {
tempcode.push(value);
}
}
}
resultcode.push(" result.push(\"" + tempcode.join("") + "\") ;");
resultcode.push("return result.join(\"\");}")
return resultcode.join("");
}
模板改成这样:
<h3>{data.key}</h3><ul>{each data.value}<li><p>{$index}:{$value.name}</p><p><dl>{each $value.record}<dd>{$value}</dd>{/each}</dl></p></li>{/each}</ul>
传入的数据:
{ key: "this is key", value: [{ name: "name1",record:[1111,2222,3333] },{ name: "name2",record:[1111,2222,3333] },{ name: "name3",record:[1111,2222,3333] }] }
渲染结果:
<h3>this is key</h3><ul><li><p>0:name1</p><p><dl><dd>1111</dd><dd>2222</dd><dd>3333</dd></dl></p></li><li><p>1:name2</p><p><dl><dd>1111</dd><dd>2222</dd><dd>3333</dd></dl></p></li><li><p>2:name3</p><p><dl><dd>1111</dd><dd>2222</dd><dd>3333</dd></dl></p></li></ul>
2.解决下层引用上层的问题:
嵌套的问题解决了,但是我们有时候在下一层还想引用上一层的变量或者数据呢,这种情况很常见吧。
在上面解决嵌套问题时,我们使用了自动生成 index/value 的方式来避免不同层 index/value 重名问题,但是我们使用的是自动生成的方式,在模板部分只能使用 $index/$value 表示,在模板层面无法区分 是哪一个层级,处理时虽然使用了自动拼接 eachIndex 区分了,但生成器生成的内容在模板是不知道的,没法引用,所以,我们前面的解决方法貌似就这样白费了。这种情况很正常,探索就是不断发现问题的过程。
这时候,笔者就想到了,如果我们在模板中指定 我们自己需要的 index/value 的名称,那在嵌套的子层要引用了上一层的变量,那直接使用自定义的 index/value 名称即可。
灵感,往往就是在你遇到死胡同时蹦出。
我们将 each 指令改成 这样一个形式:{each index,value data} ,第一个 each 用于区分这个指令,而 index,value对应这一层级的下标和值,而 data 为循环主体,其中除了 开头的 each,后面的都是可以根据具体的数据情况自定义名称的。
最后我们将方法改成如下:
function Template2code(template) {
var resultcode = [];
resultcode.push("function func(data) { var result=[];");
var tempcode = [];
var varcode = [];
var isvarcode = false;
var eachIndex = 0;
for (var key in template) {
var value = template[key];
console.log(value);
switch (value) {
case "\n":
if (isvarcode) {
varcode.push(" ")
}
else {
tempcode.push(" ");
}
break;
case "\"":
if (isvarcode) {
varcode.push("\\\"")
}
else {
tempcode.push("\\\"");
}
break;
case "{":
isvarcode = true;
resultcode.push(" result.push(\"" + tempcode.join("") + "\") ;");
tempcode = [];
break;
case "}":
isvarcode = false;
var varvalue = varcode.join("");
if (varvalue.indexOf("each") == 0) {
// each index,value arr
var eachInfo = varvalue.split(' ');
if(eachInfo.length!=3) throw "each 参数无效:each index,value arr";
var itemInfo = eachInfo[1].split(',');
if(itemInfo.length!=2) throw "each 参数无效:each index,value arr";
resultcode.push("for (var " + itemInfo[0] + " in " + eachInfo[2] + ") {var " + itemInfo[1] + " = " + eachInfo[2] + "[" + itemInfo[0] + "];");
}
else if (varvalue === "/each") {
resultcode.push(" }");
} else {
resultcode.push(" result.push(" + varvalue + ") ;");
}
varcode = [];
break;
default:
if (isvarcode) {
varcode.push(value)
}
else {
tempcode.push(value);
}
}
}
resultcode.push(" result.push(\"" + tempcode.join("") + "\") ;");
resultcode.push("return result.join(\"\");}")
return resultcode.join("");
}
模板改成这样:
<h3 class="classvalue">{data.key}</h3><ul>{each index_0,value_0 data.value}<li><p>{index_0}:{value_0.name}</p><p><dl>{each index_1,value_1 value_0.record}<dd>{value_0.name}{value_1.key}:{each index_2,value_2 value_1.value}[{value_2}]{/each}</dd>{/each}</dl></p></li>{/each}</ul>
传入数据:
{
key: "this is key", value: [{ name: "name1", record: [{ key: "语文", value: [89, 90, 78, 99] }, { key: "数学", value: ["89", "100", "78", "99"] }, { key: "英语", value: [78, 88, 96, 99] }] }, { name: "name2", record: [{ key: "语文", value: [89, 90, 78, 99] }, { key: "数学", value: [89, 100, 78, 99] }, { key: "英语", value: [78, 88, 96, 99] }] }]
}
渲染的结果:
<h3 class="classvalue">this is key</h3><ul><li><p>0:name1</p><p><dl><dd>name1语文:[89][90][78][99]</dd><dd>name1数学:[89][100][78][99]</dd><dd>name1英语:[78][88][96][99]</dd></dl></p></li><li><p>1:name2</p><p><dl><dd>name2语文:[89][90][78][99]</dd><dd>name2数学:[89][100][78][99]</dd><dd>name2英语:[78][88][96][99]</dd></dl></p></li></ul>
至此each 的循环处理算是结束了,接下来我们处理 if-else if-else
有了 each 经验 ,if 的处理简单多了。
目录: