前言
大家好,我是一名前端小学生,前端开发中框架的使用盛行如此,离不开模板引擎概念的提出,这绝对称得上是改变历史的伟大举措,那今天我们就回到一切的开始,聊聊一切的起点
什么是模板引擎
简单来讲,模板引擎就是将数据变为视图的最优雅的一种解决方案如Vue的模板语法中的v-for实际上就是一种模板语法。
他更加强调的是一种思想,即使用模板,通过数据驱动的方式来降低代码的复用,也降低数据转换为视图的难度。
实际上,其作为最优雅的方案,也是站在诸多前人的基础上的。
数据转化为视图的方案发展与优化
笔者认为,其实在前端技术的发展中,如何更为高效便捷的将数据转化为视图是广大程序员始终热议的一个话题,而诸多具有划时代意义的技术革新也与这一话题密切相关。
那我们就先来看看历史上都出现过哪些解决方案呢?
如图,从最原生的JS直接操作DOM开始,每一次迭代解决方案都在不断的向优雅二字靠拢。
其中本人认为值得一提的是数组的join方法,因为我认为,他可以说是最最最原始的模板语法思想的应用,我觉得是具有划时代意义的。
数组的join方法
这里实际上就是将HTML语句按照标签分别存为数组元素,利用Array.join将数组拼接为字符串一个大的字符串再插入body中
值得注意的是,其实这一切在ES6之后变得极为简单,
反引号的出现使得字符串可以换行,并且可以插入数据,实际上,join方法的使用也是为了解决字符串定义不可以换行的问题。
模板引擎的优秀先驱——————Mustache.js
Mustache.js的官方git仓库
mustache是“胡子”的意思,因为它的嵌入标记 {{ }} 非常像胡子。而 {{ }} 的语法也被Vue沿用,事实上,Mustache是最早的模板引擎仓库,他的底层机理在当时是极具创造性与轰动性的,以至于为后来React,Vue的发展提供了崭新的发展思路,它的出现可以说是具有里程碑意义的。
Mustache的基本使用
Mustache支持bootcdn直接引入、下载本地包或者npm引入等多种方式使用。
正如我们说的,模板思想是通过数据驱动页面的渲染,故Mustache的使用也是通过向Mustache的rende函数传入模板字符串与数据来进行使用,而render会返回将模板与数据结合好的DOM字符串(可以直接innerHTML插入的字符串)
Mustache.render(templeteStr,data)
1.简单实用
<script src="jslib/mustache.js"></script>//此处为本地包引入
<script>
var templateStr = `
<h1>我买了一个{{thing}},好{{mood}}啊</h1>
`;
var data = {
thing: '华为手机',
mood: '开心'
};
var domStr = Mustache.render(templateStr, data);
var container = document.getElementById('container');
container.innerHTML = domStr;
</script>
2.循环简单数组
<script src="jslib/mustache.js"></script>
<script>
var templateStr = `
<ul>
{{#arr}}
<li>{{.}}</li>
{{/arr}}
</ul>
`;
var data = {
arr: ['A', 'B', 'C']
};
var domStr = Mustache.render(templateStr, data);
var container = document.getElementById('container');
container.innerHTML = domStr;
</script>
3.数组嵌套
<script src="jslib/mustache.js"></script>
<script>
var templateStr = `
<ul>
{{#arr}}
<li>
{{name}}的爱好是:
<ol>
{{#hobbies}}
<li>{{.}}</li>
{{/hobbies}}
</ol>
</li>
{{/arr}}
</ul>
`;
var data = {
arr: [
{'name': '小明', 'age': 12, 'hobbies': ['游泳', '羽毛球']},
{'name': '小红', 'age': 11, 'hobbies': ['编程', '写作文', '看报纸']},
{'name': '小强', 'age': 13, 'hobbies': ['打台球']},
]
};
var domStr = Mustache.render(templateStr, data);
var container = document.getElementById('container');
container.innerHTML = domStr;
</script>
4.布尔值的使用
<script src="jslib/mustache.js"></script>
<script>
var templateStr = `
{{#m}}
<h1>你好</h1>
{{/m}}
`;
var data = {
m: false
};
var domStr = Mustache.render(templateStr, data);
var container = document.getElementById('container');
container.innerHTML = domStr;
</script>
使用布尔值可以控制页面元素的显示与隐藏,但需要注意的是虽然可以使用布尔值,但是不意味着嵌入标记内可以写表达式
Mustache.js底层机理探究
在讲原理之前,其实通过对于Mustache的简单使用之后,很多人都会和我有这样的感觉,模板引擎无非是将数据插入到有标记的位置,替换标记而已,我们需要做的无非是校验字符串与替换字符这两个方法,那么顺理成章地就会想到使用正则表达式与String.replace实现,于是有了下面的代码
实际上这样对于一些简单的dom结构的实现是可行的,但是遇到诸如上文所提到的数组嵌套时的模板解析便会十分困难,针对这个问题,Mustache提出了token的概念,整个Mustache也是围绕它所展开的
什么是token?
其实简单来讲,token就是一个嵌套的字符串数组,它是模板字符串的JS表示,但是虽然说起来容易,它的地位可是一点不轻,它就是后来抽象语法树、虚拟节点等等的开山鼻祖
我们可以举一个例子简单说明,首先我们需要一个模板字符串
<h1>我买了一个{{thing}},好{{mood}}啊</h1>
那么其实token就是以嵌入标记{{}}分割,构造数组
那么在循环情况下,就以上文对简单数组的循环为例,就会生成嵌套更深的tokens
token结合数据
当我们完成模板字符串向token的转换之后,就可以开始结合数据生成dom字符串了,这里针对复杂的数据结构,我们要提供一个查找获取数据项的函数lookup()
/**
* 用于在dataObj对象中,使用连续点符号的keyName寻找数据
* @param {object} dataObj 数据对象
* @param {string} keyName 数据属性名
*/
export default function lookup(dataObj, keyName) {
if (keyName.indexOf(".") != -1&&keyName!='.') {
var temp = dataObj;
var keys = keyName.split(".");
for (let i = 0; i < keys.length; i++) {
var temp = temp[keys[i]];
}
return temp
}
return dataObj[keyName]
}
//注意此处不是源码,只是对底层原理的简单演示,完全实现,还是要考虑很多情况的
总结
Mustache.js在底层重点只做了两件事:
① 将模板字符串编译为tokens形式
② 将tokens结合数据,解析为dom字符串
以上内容仅作个人梳理与分享,如有不对,敬请指正,多多海涵,如果觉得还可以,欢迎star哦~