Vue 源码之 mustache 模板引擎(一)
个人练习结果仓库(持续更新):Vue 源码解析
抽空把之前学的东西写成笔记。
学习视频链接:【尚硅谷】Vue 源码解析之 mustache 模板引擎
模板引擎是什么
模板引擎是将数据变为视图最优雅的解决方案。
其中,Vue 中的列表渲染指令 v-for
就是一种模板引擎。而**插值表达式{{}}`**便是本次要研究的` mustache模板引擎`的语法
## 将数据变为视图的方法
### 纯 DOM 法
很笨拙。需要频繁创建节点,添加数据,添加节点。
1 |
|
![image-20220312182355046](https://s2.loli.net/2022/03/12/chJLPs9TlfwmSey.png)
### 数组 join 法
本质上就是字符串拼接,只是用过数组 join 法,可以让结构变得更清晰
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const arr = [
{
name: "clz",
age: 21,
sex: "男",
},
{
name: "cc",
age: 21,
sex: "女",
},
{
name: "赤蓝紫",
age: 21,
sex: "男",
},
];
for (let i = 0; i < arr.length; i++) {
list.innerHTML += [
"<li>",
' <div class="hd">' + arr[i].name + "的基本信息</div>",
' <div class="bd">',
" <p>name: " + arr[i].name + "</p>",
" <p>age: " + arr[i].age + "</p>",
" <p>sex" + arr[i].sex + "</p>",
" </div>",
"</li>",
].join("");
}
### ES6 的模板字符串法
- 反引号中,文本可以直接换行
- 反引号中的${expression}占位符中 expression 可以为任意的 JavaScript 表达式,甚至为模板字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const arr = [
{
name: "clz",
age: 21,
sex: "男",
},
{
name: "cc",
age: 21,
sex: "女",
},
{
name: "赤蓝紫",
age: 21,
sex: "男",
},
];
for (let i = 0; i < arr.length; i++) {
list.innerHTML += `
<li>
<div class="hd">${arr[i].name} 的基本信息</div>
<div class="bd">
<p>name: ${arr[i].name} </p>
<p>age: ${arr[i].age} </p>
<p>sex: ${arr[i].sex} </p>
</div>
</li>
`;
}
### 模板引擎 mustache
[mustache 仓库](https://github.com/janl/mustache.js)
mustache 是**最早的模板引擎库**。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<script src="./lib/mustache.js"></script>
<script>
{{ #arr }}
{{ name }}的基本信息</div>
{{ name }} </p>
{{ age }} </p>
{{ sex }} </p>
{{ /arr }}
</script>
引入` mustache`后,就会后一个` Mustache`对象,其中有一个方法` render`就可以用来实现将数据变为视图。
- render 的第一个参数是模板字符串,第二个参数是数据
- 如果需要使用数据,直接通过` {{ }}
使用即可
- 要实现循环的话,则需要用
{{ #arr }}
,{{ /arr }}
包住要循环的内容
mustache 的基本使用
简单使用
1 |
|
循环简单数组
循环的不是对象数组,而是简单数组时,使用 .
即可
1 |
|
数组嵌套
就是上面两部分的结合版本。
1 |
|
布尔值
和循环类似,通过使用 {{#布尔值属性}}
,{{/布尔值属性}}
,包住要条件渲染的内容即可
1 |
|
通过查看 DOM 树,可以发现和 Vue 中的 v-if
指令类似,是压根就没有上 DOM 树。另外,Vue 中的 v-show
指令则是动态为元素添加或移除 display: none;
来控制元素的显示与隐藏。
es6 之前使用 mustache
众所周知,es6 之前是没有模板字符串(反引号)的。那么方便的使用 mustache 呢?
当然,可以使用上面的数组 join 法,不过,还有一个更方便的方法。
通过使用 script
标签,只要添加type
为 text/template
,然后在里面填模板字符串即可(实际上,只要不被浏览器识别即可)
1 |
|
只能说想到这个方法的人太优秀了
mustache 底层原理
正则表达式实现最简单的 mustache
String.prototype.replace()
在开始之前,首先需要了解一下字符串的 replace
方法
语法
1
str.replace(regexp|substr, newSubStr|function)
参数
regexp
(pattern):一个RegExp
对象或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。
substr
(pattern):一个将被newSubStr
替换的字符串
。其被视为一整个字符串,而不是一个正则表达式。仅第一个匹配项会被替换。
newSubStr
(replacement):用于替换掉第一个参数在原字符串中的匹配部分的字符串
。该字符串中可以内插一些特殊的变量名。参考使用字符串作为参数。
function
(replacement):一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。参考指定一个函数作为参数。返回值
一个部分或全部匹配由替代模式所取代的新的字符串。
1 |
|
可以发现,上面的做法还无法实现,所以研究一下,第二个参数为函数的情况
变量名 代表的值 match
匹配的子串。(对应于上述的$&。) p1,p2, ...
假如 replace()方法的第一个参数是一个 RegExp
对象,则代表第 n 个括号匹配的字符串。(对应于上述的$1,$2 等。)例如,如果是用/(\a+)(\b+)/
这个来匹配,p1
就是匹配的\a+
,p2
就是匹配的\b+
。offset
匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 'abcd'
,匹配到的子字符串是'bc'
,那么这个参数将会是 1)string
被匹配的原字符串。 NamedCaptureGroup 命名捕获组匹配的对象
1 |
|
可以发现,只需要在正则表达式中使用 ()
把要捕获的内容包起来,然后通过 replace
方法的函数参数中的 p1 参数获取捕获内容,既然如此,那就可以开始使用正则表达式实现简单的 mustache 了。
实现简单的 mustache
1 |
|
mustache 底层 tokens 原理
mustache 底层主要干两件事:
- 将模板字符串编译为 tokens 形式
- tokens 结合数据,解析为 dom 字符串
tokens 是什么
- tokens 是一个嵌套数组,也可以说是模板字符串的 JS 表示。
- tokens是抽象语法树(AST)、虚拟节点的开山鼻祖
看下下面的例子,就能明白了
简单 tokens
模板字符串
1 |
|
tokens
1 |
|
简单数组情况下的 tokens
模板字符串
1 |
|
tokens
1 |
|
嵌套数组情况下的 tokens
模板字符串
1 |
|
tokens
1 |
|
查看 mustache 的 tokens
进入之前下载的源码文件中, ctrl+f
,搜索 parseTemplate
,到该方法最后把返回值存好并打印
重新去跑mustache 的基本使用的代码,就可以在控制台中看到 tokens
如循环简单数组