(六)表达式解析

在前面我们将一个模板字符串转换成了DOM,但是并没有对其做任何处理,接下来要做的事情就是去识别文本插值以及各种指令。这一篇文章主要介绍使用“Mustache”语法 (双大括号) 的文本插值。

compile

首先我们要对每一个节点进行遍历,compileNodeList就是一个递归调用,如果节点下面有子节点就对其执行compileNodeList,这样我们就可以对每个节点进行compileNode处理。

compileNode中我们分别对文本节点和元素节点进行处理,这一篇文章主要讲文本插值,所以我们只看文本节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function compile(el){
if (el.hasChildNodes()) {
compileNodeList(el.childNodes)
}
}

function compileNodeList(nodeList){
var node
for (var i = 0, l = nodeList.length; i < l; i++) {
node = nodeList[i]
compileNode(node)
if (node.hasChildNodes()) {
compileNodeList(node.childNodes)
}
}
}

function compileNode (node) {
var type = node.nodeType
if (type === 3 && node.data.trim()) {
compileTextNode(node)
}
}

对文本节点的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function compileTextNode (node) {
var tokens = parseText(node.wholeText)
if (!tokens) {
return null
}
var frag = document.createDocumentFragment()
var el, token
for (var i = 0, l = tokens.length; i < l; i++) {
token = tokens[i]
if (token.tag) {
el = document.createTextNode(' ')
new Watcher(data, token.value, function(value, oldValue) {
var attr = el.nodeType === 3 ? 'data' : 'textContent';
el[attr] = value == null ? '' : value.toString()
});
} else {
el = document.createTextNode(token.value)
}
frag.appendChild(el)
}
replace(node, frag)
}

工具方法

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
function parseText (text) {
var tagRE = /\{\{((?:.|\n)+?)\}\}/g
// (?:.|\n)中,.匹配换行符以外的字符
// 因为有的系统的换行符是\r\n
// 所以.|\n匹配除了\r之外的任意字符
// ?:表示非捕获型括号,所以整个的就是匹配任意一个非\r的字符

if (!tagRE.test(text)) {
return null
}
var tokens = []
var lastIndex = tagRE.lastIndex = 0
// 在正则中lastIndex属性用于规定下次匹配的起始位置
// 因此我们每次匹配到后需要设置lastIndex为0下次才会匹配到正确的结果
var match, index, html, value
while (match = tagRE.exec(text)) {
index = match.index
// push text token
if (index > lastIndex) {
tokens.push({
value: text.slice(lastIndex, index)
})
}
// tag token
value = match[1]
tokens.push({
tag: true,
value: value.trim(),
})
lastIndex = index + match[0].length
}
if (lastIndex < text.length) {
tokens.push({
value: text.slice(lastIndex)
})
}
return tokens
}

// 较源码省略了的地方:缓存、三重大括号显示html。

Demo

查看DEMO