个人练习结果仓库(持续更新):Vue源码解析
抽空把之前学的东西写成笔记。
学习视频链接:【尚硅谷】Vue源码解析之mustache模板引擎
模板引擎是将数据变为视图最优雅的解决方案。
其中,Vue中的列表渲染指令v-for
就是一种模板引擎。而**插值表达式{{}}
**便是本次要研究的mustache模板引擎
的语法
很笨拙。需要频繁创建节点,添加数据,添加节点。
const arr = [
{
'name': 'clz',
'age': 21,
'sex': '男'
},
{
'name': 'cc',
'age': 21,
'sex': '女'
},
{
'name': '赤蓝紫',
'age': 21,
'sex': '男'
}
]
const list = document.getElementById('list')
for (let i = 0; i < arr.length; i++) {
const li = document.createElement('li') // 新建li元素
const bd = document.createElement('div')
bd.className = 'bd'
bd.innerText = arr[i].name + '的基本信息'
li.appendChild(bd)
const hd = document.createElement('div')
hd.className = 'hd'
for (const item in arr[i]) {
const p = document.createElement('p')
p.innerText = item + ': ' + arr[i][item]
hd.appendChild(p)
}
li.appendChild(hd)
list.appendChild(li)
}
本质上就是字符串拼接,只是用过数组join法,可以让结构变得更清晰
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 += [
'' ,
' ' + arr[i].name + '的基本信息',
' ',
' name: '
+ arr[i].name + '',
' age: '
+ arr[i].age + '',
' sex'
+ arr[i].sex + '',
' ',
''
].join('')
}
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 += `
${arr[i].name} 的基本信息
name: ${arr[i].name}
age: ${arr[i].age}
sex: ${arr[i].sex}
`
}
mustache仓库
mustache是最早的模板引擎库。
<script src="./lib/mustache.js">script>
<script>
// console.log(Mustache)
const templateStr = `
{{ #arr }}
-
{{ name }}的基本信息
name: {{ name }}
age: {{ age }}
sex: {{ sex }}
{{ /arr }}
`
const data = {
arr: [
{
'name': 'clz',
'age': 21,
'sex': '男'
},
{
'name': 'cc',
'age': 21,
'sex': '女'
},
{
'name': '赤蓝紫',
'age': 21,
'sex': '男'
}
]
}
const domStr = Mustache.render(templateStr, data)
document.getElementsByClassName('container')[0].innerHTML = domStr
script>
引入mustache
后,就会后一个Mustache
对象,其中有一个方法render
就可以用来实现将数据变为视图。
{{ }}
使用即可{{ #arr }}
,{{ /arr }}
包住要循环的内容mustache.js
const templateStr = `
我是{{name}}, 年龄为{{age}}岁
`
const data = {
name: 'clz',
age: 21
}
const domStr = Mustache.render(templateStr, data)
document.getElementsByClassName('container')[0].innerHTML = domStr
循环的不是对象数组,而是简单数组时,使用.
即可
const templateStr = `
{{#arr}}
{{.}}
{{/arr}}
`
const data = {
arr: ['red', 'blue', 'purple']
}
const domStr = Mustache.render(templateStr, data)
document.getElementsByClassName('container')[0].innerHTML = domStr
就是上面两部分的结合版本。
const templateStr = `
{{#arr}}
-
{{name}}喜欢的颜色是:
{{#colors}}
- {{.}}
{{/colors}}
{{/arr}}
`
const data = {
arr: [
{
name: 'clz',
colors: ['red', 'blue', 'purple']
},
{
name: 'cc',
colors: ['white', 'red', 'black']
}
]
}
const domStr = Mustache.render(templateStr, data)
document.getElementsByClassName('container')[0].innerHTML = domStr
和循环类似,通过使用{{#布尔值属性}}
,{{/布尔值属性}}
,包住要条件渲染的内容即可
const templateStr = `
{{#arr}}
{{#show}}
{{name}}
{{/show}}
{{/arr}}
`
const data = {
arr: [
{
name: 'clz',
show: true
},
{
name: 'czh',
show: false
}
]
}
const domStr = Mustache.render(templateStr, data)
document.getElementsByClassName('container')[0].innerHTML = domStr
通过查看DOM树,可以发现和Vue中的v-if
指令类似,是压根就没有上DOM树。另外,Vue中的v-show
指令则是动态为元素添加或移除display: none;
来控制元素的显示与隐藏。
众所周知,es6之前是没有模板字符串(反引号)的。那么方便的使用mustache呢?
当然,可以使用上面的数组join法,不过,还有一个更方便的方法。
通过使用script
标签,只要添加type
为text/template
,然后在里面填模板字符串即可(实际上,只要不被浏览器识别即可)
<script type="text/template" id="templateStr">
{{#arr}}
{{#show}}
<h2>{{name}}</h2>
{{/show}}
{{/arr}}
</script>
<script src="./lib/mustache.js"></script>
<script>
const templateStr = document.getElementById('templateStr').innerHTML
const data = {
arr: [
{
name: 'clz',
show: true
},
{
name: 'czh',
show: false
}
]
}
const domStr = Mustache.render(templateStr, data)
document.getElementsByClassName('container')[0].innerHTML = domStr
</script>
只能说想到这个方法的人太优秀了
在开始之前,首先需要了解一下字符串的replace
方法
语法
str.replace(regexp|substr, newSubStr|function)
参数
regexp
(pattern):一个RegExp
对象或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。
substr
(pattern):一个将被newSubStr
替换的字符串
。其被视为一整个字符串,而不是一个正则表达式。仅第一个匹配项会被替换。
newSubStr
(replacement):用于替换掉第一个参数在原字符串中的匹配部分的字符串
。该字符串中可以内插一些特殊的变量名。参考使用字符串作为参数。
function
(replacement):一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。参考指定一个函数作为参数。返回值
一个部分或全部匹配由替代模式所取代的新的字符串。
const templateStr = `
我是{{name}}, 年龄为{{age}}岁
`
const data = {
name: 'clz',
age: 21
}
console.log(templateStr.replace(/\{\{\w+\}\}/g, '123'))
可以发现,上面的做法还无法实现,所以研究一下,第二个参数为函数的情况
变量名 代表的值 match
匹配的子串。(对应于上述的$&。) p1,p2, ...
假如replace()方法的第一个参数是一个 RegExp
对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)例如,如果是用/(\a+)(\b+)/
这个来匹配,p1
就是匹配的\a+
,p2
就是匹配的\b+
。offset
匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 'abcd'
,匹配到的子字符串是'bc'
,那么这个参数将会是 1)string
被匹配的原字符串。 NamedCaptureGroup 命名捕获组匹配的对象
const templateStr = `
我是{{name}}, 年龄为{{age}}岁
`
const data = {
name: 'clz',
age: 21
}
templateStr.replace(/\{\{(\w+)\}\}/g, (match, p1, offset, string) => {
console.log(match)
console.log(p1)
console.log(offset)
console.log(string)
})
可以发现,只需要在正则表达式中使用()
把要捕获的内容包起来,然后通过replace
方法的函数参数中的p1参数获取捕获内容,既然如此,那就可以开始使用正则表达式实现简单的mustache了。
const templateStr = `
我是{{name}}, 年龄为{{age}}岁
`
const data = {
name: 'clz',
age: 21
}
const render = (templateStr, data) => {
return templateStr.replace(/\{\{(\w+)\}\}/g, function (match, p1, offset, string) {
return data[p1] // 把正则所匹配的内容替换成return的内容
})
}
const domStr = render(templateStr, data)
document.querySelector('.container').innerHTML = domStr
mustache底层主要干两件事:
看下下面的例子,就能明白了
简单tokens
模板字符串
<h2>我是{{name}}, 年龄为{{age}}岁h2>
tokens
[
["text", "我是"
],
["name", "name"],
["text", ", 年龄为"],
["name", "age"],
["text", "岁"]
]
简单数组情况下的tokens
模板字符串
{{#arr}}
<h2 style="color: {{.}}">{{.}}h2>
{{/arr}}
tokens
[
["#", "arr", [
["text": ""
],
["name", "."],
["text", ""]
]]
]
嵌套数组情况下的tokens
模板字符串
<ul>
{{#arr}}
<li>
{{name}}喜欢的颜色是:
<ol>
{{#colors}}
<li>{{.}}li>
{{/colors}}
ol>
li>
{{/arr}}
ul>
tokens
[
["text", ""
],
["#", "arr", [
["text", "li"],
["name", "name"],
["text": "喜欢的颜色是:"
],
["#", "colors", [
["text", "" ],
["name", "."],
["text", ""]
]],
["text", ""]
]],
["text", ""]
]
进入之前下载的源码文件中,ctrl+f
,搜索parseTemplate
,到该方法最后把返回值存好并打印
重新去跑mustache的基本使用的代码,就可以在控制台中看到tokens
如循环简单数组