使用普通 HTML Template 与 JavaScript 的方式开发 Riot.js

Riot.js 是一个像 React 的 UI 库,压缩版本 17 KB,Gzip 后 3.5 KB,可以作为 MVVM 模式中的 VM 模块使用。

Riot.js 可以为包括 IE 8 在内的各大浏览器提供自定义的标签。想象 React + Polymer 却拥有易用且低学习成本的语法。

通常,一个 Riot 标签(.tag 文件)形如下方:

todo.tag

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
<todo>
<h3>{ opts.title }</h3>
<ul>
<li each="{ items.filter(whatShow) }">
<label class="{ completed: done }">
<input type="checkbox" checked="{ done }" onclick="{ parent.toggle }"> { title }
</label>
</li>
</ul>
<form onsubmit="{ add }">
<input name="input" onkeyup="{ edit }">
<button disabled="{ !text }">Add #{ items.filter(whatShow).length + 1 }</button>
<button disabled="{ items.filter(onlyDone).length == 0 }" onclick="{ removeAllDone }">
X{ items.filter(onlyDone).length } </button>
</form>
<!-- this script tag is optional -->
<script>
this.items = opts.items
edit(e) {
this.text = e.target.value
}
add(e) {
if (this.text) {
this.items.push({ title: this.text })
this.text = this.input.value = ''
}
}
removeAllDone(e) {
this.items = this.items.filter(function(item) {
return !item.done
})
}
// an two example how to filter items on the list
whatShow(item) {
return !item.hidden
}
onlyDone(item) {
return item.done
}
toggle(e) {
var item = e.item
item.done = !item.done
return true
}
</script>
</todo>

其实我们可以看做其是编写自定义标签的语法糖,然而目前却没有对 Riot.js 的 tag 语法支持得比较好的编辑器

因此,我们可以尝试用普通 HTML 模板 + JavaScript(CoffeeScript、ES6 或者任何你喜欢的语法)的方式来进行 Riot.js 的开发。

从官方的编译成 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
riot.tag('todo', '<h3>{ opts.title }</h3> <ul> <li each="{ items.filter(filter) }"> <label class="{ completed: done }"> <input type="checkbox" __checked="{ done }" onclick="{ parent.toggle }"> { title } </label> </li> </ul> <form onsubmit="{ add }"> <input name="input" onkeyup="{ edit }"> <button __disabled="{ !text }">Add #{ items.filter(filter).length + 1 }</button> </form> ', function(opts) {
this.items = opts.items
this.edit = function(e) {
this.text = e.target.value
}.bind(this);
this.add = function(e) {
if (this.text) {
this.items.push({ title: this.text, done: false, hidden: false })
this.text = this.input.value = ''
}
}.bind(this);
this.filter = function(item) {
return !item.hidden
}.bind(this);
this.toggle = function(e) {
var item = e.item
item.done = !item.done
return true
}.bind(this);
});

boolean 类型的属性,需要在前方加上双下划线 __,其他模板代码像普通 HTML 一样书写就可以了。

注意双向绑定请一定加上事件监听,以使 Riot.js 建立对其事件的更新。

我们可以像下面这样在 HTML 中写模板代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
<todo>
<h3>{opts.title}</h3>
<ul>
<li each="{items.filter(filter)}">
<label class="{completed: done}">
<input type="checkbox" __checked="{done}" onchange="{parent.toggle}"> {title}
</label>
</li>
</ul>
<form onsubmit="{add}"> <input name="input" oninput="{edit}">
<button __disabled="{!input.value}">Add #{items.filter(filter).length + 1}</button>
</form>
</todo>

是不是感觉与编写 AngularJS 模板非常类似。

JavaScript 的部分依然很简单,与在 tag 文件中编写几乎一样。

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
var tName = 'todo'
var t = document.getElementsByTagName(tName)
riot.tag(tName, t[0].innerHTML, function(opts) {
var _this = this;
_this.items = opts.items;
_this.edit = function(e) {
// 此处无操作,仅仅为了绑定一个函数,让 Riot.js 在触发事件时进行更新
};
_this.add = function(e) {
if (_this.input.value) {
_this.items.push({
title: _this.input.value,
done: false,
hidden: false
});
_this.input.value = '';
}
};
_this.filter = function(item) {
return !item.hidden;
};
_this.toggle = function(e) {
var item = e.item;
item.done = !item.done;
return true;
};
});

然后像普通标签那样挂载它:

1
2
3
4
5
6
7
8
9
riot.mount('todo', {
title: 'I want to behave!',
items: [
{ title: 'Avoid excessive coffeine', done: true, hidden: false },
{ title: 'Hidden item', done: false, hidden: true },
{ title: 'Be less provocative', done: false, hidden: false },
{ title: 'Be nice to people', done: false, hidden: false }
]
});

大功告成,妈妈再也不用担心我没有合适的编辑器来开发 Riot.js 了。

查看完整效果与代码