列表模板引擎进化之路

平时的工作中,经常会遇到渲染列表的需求,比如下面的代码

1
2
3
4
5
6
7
8
9
var data = [{id: 1, name: 'hanger'}, {id: 2, name: 'Alice'}]
var tableContent = ''
data.forEach(function (obj) {
tableContent += '<tr>' +
'<th >' + obj.id + '</th>' +
'<th>' + obj.name + '</th>' +
'</tr>'
})
console.log(tableContent)

向上面这样写一个字符串十分繁琐,而且不可复用。那么有什么办法可以解决这样的问题呢?
我们都知道js的replace()函数可以实现字符串的替换,replace()第二个参数可以为函数,在开启列表模板引擎进化之旅之前需要先理解replace方法
现在我要利用replace()的强大能力来解决上面这个问题。直接上最初的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function tplEngine1(tpl, obj) {
var tpl = tpl.replace(/<%([^%>]+)?%>/g, function(a, b) {
var b = b.trim()
return obj[b]
})
return tpl
}
var tpl = '<tr>' +
'<th><%id%></th>' +
'<th><%name%></th>' +
'</tr>'
var tableContent = ''
data.forEach(function (obj) {
tableContent += tplEngine1(tpl, obj)
})
console.log(tableContent)

在控制台打印一下,你会得到同样的结果,是不是觉得这样的字符串简单多了呢。
但是上面这个版本的模板引擎还是最简单的、功能最弱的版本,随着需求的增加,我们需要不断改进它。

解决两层数据对象

1
2
3
4
5
6
7
8
9
10
11
12
function tplEngine2(tpl, obj) {
var tpl = tpl.replace(/<%([^%>]+)?%>/g, function(a, b) {
var b = b.trim()
if (/\./.test(b)) {
var arr = b.split('.')
return obj[arr[0]][arr[1]]
} else {
return obj[b]
}
})
return tpl
}

解决得到的数据是数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function tplEngine3(tpl, obj, separator) {
var separator = separator || ','
var tpl = tpl.replace(/<%([^%>]+)?%>/g, function(a, b) {
var b = b.trim()
if (/\./.test(b)) {
var arr = b.split('.')
if (obj[arr[0]][arr[1]] instanceof Array) {
return obj[arr[0]][arr[1]].join(separator)
} else {
return obj[arr[0]][arr[1]]
}
} else {
if (obj[b] instanceof Array) {
return obj[b].join(separator)
} else {
return obj[b]
}
}
})
return tpl
}

避免显示undefined

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
function tplEngine4(tpl, obj, fruitless, separator) {
var fruitless = fruitless || ''
var separator = separator || ','
var tpl = tpl.replace(/<%([^%>]+)?%>/g, function(a, b) {
var b = b.trim()
if (/\./.test(b)) {
var arr = b.split('.')
if (obj[arr[0]][arr[1]] instanceof Array) {
if (obj[arr[0]][arr[1]].length == 0) {
return fruitless
} else {
return obj[arr[0]][arr[1]].join(separator)
}
} else {
return obj[arr[0]][arr[1]]
if (obj[arr[0]][arr[1]]) {} else {
return fruitless
}
}
} else {
if (obj[b] instanceof Array) {
if (obj[b].length == 0) {
return fruitless
} else {
return obj[b].join(separator)
}
} else {
if (obj[b]) {
return obj[b]
} else {
return fruitless
}
}
}
})
return tpl
}

填充变量

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
function tplEngine5(tpl, obj, fruitless, separator) {
var fruitless = fruitless || ''
var separator = separator || ','
var tpl = tpl.replace(/<@([^@>]+)?@>/g, function(match, $1) {
var $1 = $1.trim()
return eval($1)
})
var tpl = tpl.replace(/<%([^%>]+)?%>/g, function(a, b) {
var b = b.trim()
if (/\./.test(b)) {
var arr = b.split('.')
if (obj[arr[0]][arr[1]] instanceof Array) {
if (obj[arr[0]][arr[1]].length == 0) {
return fruitless
} else {
return obj[arr[0]][arr[1]].join(separator)
}
} else {
return obj[arr[0]][arr[1]]
if (obj[arr[0]][arr[1]]) {} else {
return fruitless
}
}
} else {
if (obj[b] instanceof Array) {
if (obj[b].length == 0) {
return fruitless
} else {
return obj[b].join(separator)
}
} else {
if (obj[b]) {
return obj[b]
} else {
return fruitless
}
}
}
})
return tpl
}

添加过滤函数

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
function tplEngine6(tpl, data, separator) {
// 初始化参数
if (!separator && separator !== 0) {
separator = ','
}

// 输出函数
function print($1, filter) {
if (/\./.test($1)) {
var arr2 = $1.split('.')
var arr3 = data[arr2[0]][arr2[1]]
if (arr3 instanceof Array) {
if (filter) {
for (var i = 0; i < arr3.length; i++) {
arr3[i] = filter(arr3[i])
}
}
return arr3.join(separator)
} else {
if (filter) {
arr3 = filter(arr3)
}
return arr3
}
} else {
var arr4 = data[$1]
if (arr4 instanceof Array) {
if (filter) {
for (var i = 0; i < arr4.length; i++) {
arr4[i] = filter(arr4[i])
}
}
return arr4.join(separator)
} else {
if (filter) {
arr4 = filter(arr4)
}
return arr4
}
}
}

// 变量填充
var tpl = tpl.replace(/<@([^@>]+)?@>/g, function(match, $1) {
var $1 = $1.trim()
if (/\|/.test($1)) {
var arr = $1.split('|')
var filter = eval(arr[1])
var variate = eval(arr[0])
return filter(variate)
} else {
return eval($1)
}
})

// 属性填充
var tpl = tpl.replace(/<%([^%>]+)?%>/g, function(match, $1) {
var $1 = $1.trim()
if (/\|/.test($1)) {
var arr = $1.split('|')
var filter = eval(arr[1])
return print(arr[0], filter)
} else {
return print($1)
}
})
return tpl
}

有了过滤函数,就可以处理没值的情况了,因此去掉了fruitless参数,封装了print()函数使代码更加简洁。至此,列表模板引擎已经可以处理大部分工作中遇到的情况了。