「这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」。
手写将tokens嵌套起来
新增nestTokens.js
我们如果有一些嵌套循环,如图:
那之前的方法就行不通了,因为有多个#和.。
这里我们要引用一种数据结构叫做栈。先进后出(First In Last Out,FILO),就可以实现嵌套。
我们期望的结果:
分析
- 首先,我们需要一个整合最后结果的数组,我们命名为nestedTokens;
- 然后,我们需要一个可以存放栈结构的变量,我们命名为section;
- 再然后,我们还需要一个可以指向原数组第三项(即新创建的数组)的变量,我们命名为collector;
- 最后,我们需要一个记录当前子数组的变量,我们命名为token 完整代码 nestedTokens.js:
export default function nestTokens(tokens) {
var nestedTokens = [];
var section = [];
var collector = nestedTokens;
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i];
if (token[0] == "#") {
collector.push(token);
section.push(token);
collector = token[2] = [];
} else if (token[0] == "/") {
section.pop();
collector = section.length > 0 ? section[section.length - 1][2] : nestedTokens;
} else {
collector.push(token);
}
}
return nestedTokens;
}
var collector = nestedTokens:可以保证collector指向的数组与最后需要返回的数组是同一个数组。
collector = token[2] = []:这一行代码,也就是让collector指向第一项为#的子数组,因为带有#的数组说明他需要遍历出一个子数组,我们先用collector指向它,说明它后面需要结束遍历。
也就是图中框出来的即将诞生的第三项 => token[2]
collector = section.length > 0 ? section[section.length - 1][2] : nestedTokens:结束这一层遍历,返回上一层继续遍历直至结束。
一些修改
templateTokens.js的返回值要更改成为nestToken(tokens)了,也就是要返回已经嵌套好的数组了(记得要import新增的函数).
将tokens注入数据
我们分为:
- tokens[0] == text的普通字符串,直接连接;
- token[0] == name的数据,需要注入后连接;
- token[0] == #的数组,需要循环后连接; 这里我们需要注意一点,我们的属性有可能是嵌套对象,即对象属性全称是a.b.c,这个时候如果我们直接通过data["a.b.c"]的方法来配对数据会发现得到undified。
因此我们需要构建一个新的函数,来拆分属性名中带有"."的属性。(这个地方很有可能成为面试题,即拆分属性名得到数据中对应的属性值)
export default function lookUp(dataObj, keyName) {
// 先判断属性名中是否包含点符号
if (keyName.indexOf(".") != -1 && keyName != ".") {
var keys = keyName.split(".");
var temp = dataObj;
for (let i = 0; i < keys.length; i++) {
temp = temp[keys[i]];
};
return temp;
}
return dataObj[keyName];
}
思路:先将譬如"a.b.c"这一类的属性名拆分成数组,在数据对象dataObj中层层向下寻找,直到找到属性名为c的属性对应的属性值。
将tokens数组变为dom字符串
renderTemplate.js:
export default function renderTemplate(tokens, data) {
var resultStr = "";
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i];
if (token[0] == "text") {
resultStr += token[1];
} else if (token[0] == "name") {
resultStr += lookUp(data, token[1]);
} else if (token[0] == "#") {
resultStr += parseArray(token, data);
}
}
return resultStr;
}
思路:判断每个tokens的子数组的token[0]是text还是name,还是#,如果是text,则将对应的token[1]连接到结果字符串上;如果是name,则可以通过上面的lookUp函数返回对应的那一段字符串;如果是#,这个时候就说明有数组需要遍历取值了,则和下面的函数利用递归得到对应字符串。
通过递归实现嵌套数组的数据注入
parseArray.js:
export default function parseArray(token, data) {
var v = lookUp(data, token[1]);
var resultStr = "";
for (let i = 0; i < v.length; i++) {
resultStr += renderTemplate(token[2], {
...v[i],
".": v[i]
});
}
return resultStr;
}
思路:这个函数的作用就是得到子数组对应的那一段字符串。这里要注意,传入的第一个参数是token,而不是tokens,也就是说这一个函数只对包含数组的token子数组有效。
var v = lookUp(data, token[1]):这一句的作用是,得到数组名称对应的数组,这里一定得到的是数组而不是一个值。原因是只有包含数组的token我们才调用这个函数来注入数据。
最终的index.js只有几行:
window.templateEngine = {
render(templateStr, data) {
var tokens = templateToTokens(templateStr);
var domStr = renderTemplate(tokens, data);
return domStr;
},
}
var tokens = templateToTokens(templateStr) -- 调用templateToTokens函数,将模板字符串变为tokens数组;
var domStr = renderTemplate(tokens, data) -- 调用renderTemplate函数,将tokens数组变为dom字符串。
最后!!!来运行!!!!
以前内容: