好,先来举个栗子,先定义一个父标签<account>,然后再来一个嵌套的标签<subscription>。
<account> <subscription plan={ opts.plan } show_details="true" /> </account> <subscription> <h3>{ opts.plan.name }</h3> // Get JS handle to options var plan = opts.plan, show_details = opts.show_details // access to the parent tag var parent = this.parent </subscription>
重要 注意在这里,show_details属性我们用的是下划线命名法而不是驼峰,因为浏览器会自动把驼峰换成全小写
好,接下来把account标签mount到一个有计划设置选项的页面
<body> <account> </account> </body> <script> riot.mount('account', { plan: { name: 'small', term: 'monthly' } }) </script>
父标签选项会被riot.mount方法传递,子标签选项会被传递到标签属性上
重要 嵌套标签默认会内部声明一个父级自定义标签。如果他们在页面中定义的话呢是不会初始化的
“HTML transclusion”是用来处理内部HTML的方法,这是由一个内置的<yield>实现的。
<my-tag> <p>Hello <yield/></p> this.text = 'world' </my-tag>
自定义标签和嵌套HTML一起被置于页面上
<my-tag> <b>{ text }</b> </my-tag>
<my-tag> <p>Hello <b>world</b><p> </my-tag>
在后面的API翻译文档中会详细说明这个yield
有name和id属性的元素会被自动绑定到上下文,所以用JS访问他们会很容易
<login> <form id="login" onsubmit={ submit }> <input name="username"> <input name="password"> <button name="submit"> </form> // grab above HTML elements var form = this.login, username = this.username.value, password = this.password.value, button = this.submit </login>
当然这些命名元素也能被单独嵌进HTML,像这样:
<div>{ username.value }</div>
一个DOM事件处理函数被称为“事件处理器”,定义如下:
<login> <form onsubmit={ submit }> </form> // this method is called when above form is submitted submit(e) { } </login>
on开头的属性会在被触发时接受一个函数值,这个函数也可以在表达式里定义
<form onsubmit={ condition ? method_a : method_b }>
在函数里这是指当前标签实例,在处理器之后会调用this.update(),这个API会自动把所有可能的改变映射到UI里
除非元素是chekbox或radio,否则默认的事件处理程序的行为会被自动取消。这意味着e.preventDefault()已经被你调用了,因为通常你都会这么做(或者你只是忘记了。。),不过你可以通过让浏览器在处理器上返回true来做默认的事情:
submit() { return true }
事件处理器接受标准的事件对象作为第一个参数,下面这些是跨浏览器的
e.currentTarget 指向事件处理器明确的标签
e.target 是个原始元素。不一定和currentTarget一样哈
e.which 是键盘事件(keypress, keyup什么的)的关键码
e.item 是循环中的当前元素,后面会详讲
条件语句让你根据条件来展示或隐藏元素
<div if={ is_premium }> <p>This is for premium users only</p> </div>
同样,表达式可以只是一个简单的属性。也可以是一个完整的JS表达式,现在一共有这些特殊属性:
show — 当 style="display: ' '" 为true时展示元素
hide — 当 style="display: none" 为true时隐藏元素
if — true时增加元素到文档,false反之
等于运算符是==而不是===,比如 'a string' == true
带 each 属性的循环是这么用滴:
<todo> <ul> <li each={ items } class={ completed: done }> <input type="checkbox" checked={ done }> { title } </li> </ul> this.items = [ { title: 'First item', done: true }, { title: 'Second item' }, { title: 'Third item' } ] </todo>
带each的那个属性的将遍历数组中的所有item
新的上下文会为每一个item创建。这些是标签实例。当循环嵌套的时候,所有在循环中的自标签将继承所有父循环中自己尚未定义的属性和方法。通过这种方法,Riot避免了本不该由父标签随意覆盖的东西
父级可以通过父变量来显式访问
<todo> <div each={ items }> <h3>{ title }</h3> <a onclick={ parent.remove }>Remove</a> </div> this.items = [ { title: 'First' }, { title: 'Second' } ] remove(event) { } </todo>
在loop过的元素中,除了 each 属性,其他一切均属于子级上下文。这样标题就可以直接访问和移除需要给父级加前缀。因为这方法不是循环item的一个属性。
这些循环item都是标签实例,Riot不会碰原始item,所以他们不会被增加任何新的属性
事件处理器可以用一个带 event.item 的 collection 访问各个项目。好,我们来实现下这个remove功能:
<todo> <div each={ items }> <h3>{ title }</h3> <a onclick={ parent.remove }>Remove</a> </div> this.items = [ { title: 'First' }, { title: 'Second' } ] remove(event) { // looped item var item = event.item // index on the collection var index = this.items.indexOf(item) // remove from collection this.items.splice(index, 1) } </todo>
事件处理器处理后当前标签实例就会被 this.update() Update(除非你像上面一样用返回true设置e.preventUpdate),witch导致所有循环item也被执行。当一个item被从collection移除或者对应的DOM节点被移除时,父级会提醒那个item。
自定义标签也是可以被循环哒:
<todo-item each="{ items }" data="{ this }"></todo-item>
当前循环的item可以加注一个 this ,如果你需要把这个item作为一个选项传递给循环过的标签的话
数组元素里不一定需要对象,也可以是字符串或者数字啊什么的。在下面这个例子里你需要用这种结构 { name, i in items } :
<my-tag> <p each="{ name, i in arr }">{ i }: { name }</p> this.arr = [ true, 110, Math.random(), 'fourth'] </my-tag>
这个 name 是这个元素的名字,这个 i 是它的索引号。所有的标签都可以是任何东西,只要适合就好
清晰的对象也可以被循环的:
<my-tag> <p each="{ name, value in obj }">{ name } = { value }</p> this.obj = { key1: 'value1', key2: 1110.8900, key3: Math.random() } </my-tag>
不过呢不推荐用这种用法,因为在Riot内部是用 JSON.stringify 发现对象的改变的。整个对象都会被搜一遍,当有任何改变时,整个对象都会被预渲染。这样的话性能就。。。正常的数组速度快得多,并且只有改变的部分会被映射到页面上
嗯,标准的HTML元素也是可以变成Riot专用标签的,只要加一个 riot-tag 属性就好
<ul riot-tag="my-tag"></ul>
这给了亲们一种非主流的、可以和其他CSS框架更好融合的用法。这时这些标签会被当成自定义标签
riot.mount('my-tag')
就像这样mount就好,那个ul会被当成
<my-tag></my-tag>
Riot可以在服务器端做个预编译(预渲染)来提高性能,当然也强烈建议这么做!
用node.js/io.js:
var riot = require('riot') var timer = require('timer.tag') var html = riot.render(timer, { start: 42 }) console.log(html)//<timer><p>Seconds Elapsed: 42</p></timer>
loop和conditional也支持的哦