持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
温馨提示:本篇文章对源码初学者不是很友好,若是从零开始学习源码,建议自行上网寻找视频教程
1. 如何从data数据生成DOM节点树?
-
纯原生JS语法创建创建DOM
利用原生JS中的createElement创建节点,将data中的数据写入其innerText中 并且利用appendChild方法插入到页面中的DOM容器,过程非常繁琐,需要一个个节点创建创建并插入
-
利用数组的join方法
其前身就是通过创建字符串,并且赋值给容器的innerHTML,但是对程序员的编写不友好,格式丑 为了解决格式的问题,(大佬)发现可以利用数组的join方法,数组的每一个元素占一行, 从而让编写的字符串有了结构,最后通过join的方法再拼接成字符串,再赋值给innerHTML
-
ES6模板字符串
作为新兴的ES6语法,模板字符串出现让程序员的编码效率空前提高 因为与原来的字符串相比,模板字符串支持跨行,并且占位符‘$’也能够减少很多代码量,避免了繁琐的字符串拼接
2. mustache库的基本使用
- 如何定义模板字符串
- 示例
var templateStr = `
<ul>
{{#students}}
<li>
<div id="hd">{{name}}的信息</div>
<div id="bd">
<p>姓名:{{name}}</p>
<p>年龄:{{age}}</p>
<p>性别:{{sex}}</p>
</div>
</li>
{{/studnets}}
</ul>
`
- 若需遍历数组,利用‘#’符号和‘/’符号表示遍历的区间,在区间的内部则可以直接使用 {{}} 直接读取每个元素中的属性
- 若只是遍历简单数组,遍历区间的内部可直接通过 {{.}} 来读取
- 该语法也支持嵌套遍历数组
- 如果 # 后面是一个布尔值,将决定该区间是否上树
- 通过Mustache中的render函数将模板字符串转换为节点字符串
- 示例:
var domStr = Mustache.render(templateStr, data)
- 需要传入两个参数,一个是模板字符串templateStr,一个是数据data
- 转换为节点字符串后即可将其赋值给容器的innerHTML
- 最简单的模板引擎实现机理:正则表达式
- 示例:
function render(templateStr, data) {
return templateStr.replace(/{{(\w+)}}/g, function (findStr, $1) {
return data[$1]
})
}
- 通过正则表达式将 {{}} 中替换成data中的真实数据,但是只支持替换结构简单的data
3. 手写低配版的模板引擎
-
该模板引擎需要实现的功能包括两步:
- 将模板字符串转换为tokens
- 将tokens结合data转换为DOM字符串
- 将模板字符串转换为tokens(parseTemplateToTokens.js)
- tokens的格式:
[ ['text','<ul>'],
['#','students',[ ['text','<li> <div id="hd">'],
['name','name'],
['text','的信息</div> <div id="bd"> <p>姓名:'],
['name','name'],
['text','</p> <p>年龄:'],
['name','age'],
['text','</p> <p>性别:'],
['name','sex'],
['text','</p> </div> </li>']
]],
['text','</ul>']
]
-
具体实现:
-
以
{{和}}为界限,双花括号外部的是text,而内部的是name或#,这个需要判断 {{ 的下一位是不是#,然后存储在tokens数组中(Scanner.js文件) -
需要将经过上一步处理返回的
tokens进一步处理,因为需要将#遍历区间的token折叠,即从一维变为嵌套数组如果遍历到
#就先在这个节点创建一个空数组以存储后面的子节点,这个过程利用到栈结构,所以支持嵌套折叠,在遍历到/时则这一层的遍历结束在遍历的过程中也需要时刻检查栈内是否还有元素,从而决定元素是应该插入到子数组中还是tokens总数组中(
nestTokens.js文件)
-
- 将tokens结合data转换为DOM字符串(renderTemplate)
- 需要将
tokens中的每一项结合data转换为DOM字符串 - 如果是
text项,直接将token[1]追加入DOM字符串中 - 如果是
name项,则需要将data中对应的数据追加入DOM字符串中(lookup.js文件)
如果token[1]中有 . 的形式,则需要利用split的方法将字符串根据 . 拆分为数组,并且逐个读取data深层数据,最终追加入DOM字符串中 - 如果是
#项,则需要递归调用renderTemplate函数:
for(let i = 0; i < v.length; i++){
resultStr += renderTemplate(token[2],{
...v[i],
'.':v[i]
})
}
- 其中
...展开符跟直接传入data中需要的数组效果一样,这么写是为了加入.属性,因为存在遍历最普通一维数组的情况
- 最终将生成的DOM字符串作为返回值返回,即可将其插入到容器的innerHTML