前端孟抗,你是文藝界的程序員
我為什么要用Riot
?優(yōu)點(diǎn)明顯润匙,體積小惊暴,加載快,繼承了react趁桃,Polymer等框架的優(yōu)勢(shì),并簡(jiǎn)化他們肄鸽,主要從以下幾個(gè)方面考慮:
1. 自定義標(biāo)簽卫病。
?Riot 在所有瀏覽器上支持自定義標(biāo)簽,我們能將頁面組件化典徘,一個(gè)自定義標(biāo)簽結(jié)構(gòu)如下所示:
<todo>
<!--布局-->
<h3>{ item }</h3>
<!-- css -->
<style scoped>
h3 { font-size: 14px; }
</style>
<!-- 業(yè)務(wù)邏輯 -->
<script>
var self = this;
self.item = ‘Riot sample’;
</script>
</todo>
2. 對(duì)閱讀友好
?有了自定義標(biāo)簽功能后蟀苛,我們可以用很簡(jiǎn)潔語言‘拼湊’出復(fù)雜的用戶界面,加上語義化標(biāo)簽定義逮诲,在閱讀的時(shí)候帜平,很容易看清楚哪個(gè)標(biāo)簽是給html加入了什么組件,一共有多少個(gè)組件梅鹦,這樣一個(gè)頁面大致實(shí)現(xiàn)什么功能裆甩,甚至不用看瀏覽器展示都能明白。你的代碼可能是這樣的
<body>
<h1>Acme community</h1>
<forum-header/>
<forum-content>
<forum-threads/>
<forum-sidebar/>
</forum-content>
<forum-footer/>
<script>riot.mount('*', { api: forum_api })</script>
</body>
?很清楚齐唆,頁面被分成四三個(gè)模塊嗤栓,一個(gè)標(biāo)題h1
,一個(gè)header
,一個(gè)content
和一個(gè)footer
茉帅,這樣就在我們腦海構(gòu)成一個(gè)基礎(chǔ)的網(wǎng)頁模型
?Riot 標(biāo)簽首先被 編譯 成標(biāo)準(zhǔn) JavaScript叨叙,然后在瀏覽器中運(yùn)行。
3. 虛擬 DOM
- 保證最少量的DOM 更新和數(shù)據(jù)流動(dòng)
- 單向數(shù)據(jù)流: 更新和刪除操作由父組件向子組件傳播
- 表達(dá)式會(huì)進(jìn)行預(yù)編譯和緩存以保證性能
- 為更精細(xì)的控制提供生命周期事件
- 支持自定義標(biāo)簽的服務(wù)端渲染堪澎,支持單語言應(yīng)用
4. 與標(biāo)準(zhǔn)保持一致
- 沒有專有的事件系統(tǒng)
- 渲染出的 DOM 節(jié)點(diǎn)可以放心地用其它工具(庫)進(jìn)行操作
- 不要求額外的 HTML 根元素或
data-
屬性 - 與 jQuery 友好共存
5. 友好的語法
(1).強(qiáng)大的簡(jiǎn)寫語法
class={ enabled: is_enabled, hidden: hasError() }
(2).語義化強(qiáng)擂错,不需要費(fèi)腦記憶
render, state, constructor 或 shouldComponentUpdate
(3).直接插值
Add #{ items.length + 1 } 或 class="item { selected: flag }"
(4).用 <script>
標(biāo)簽來包含邏輯代碼不是必需的
(5).緊湊的 ES6
方法定義語法
6. 麻雀小,五臟全
最小化是 Riot 區(qū)別于其它庫的重要特點(diǎn)樱蛤,它所提供的 API 方法比其他庫要少 10 至 100倍钮呀。
?react.min.js – 34.89KB
?polymer.min.js – 49.38KB
?riot.min.js – 10.38KB
Riot 擁有創(chuàng)建現(xiàn)代客戶端應(yīng)用的所有必需的成分:
- “響應(yīng)式” 視圖層用來創(chuàng)建用戶界面
- 用來在各獨(dú)立模塊之間進(jìn)行通信的事件庫
- 用來管理
URL
和瀏覽器回退按鈕的路由器(Router
)
?
學(xué)前三兩個(gè)FAQ
1. 標(biāo)簽名中必須使用橫線 (-) 嗎?
?W3C 規(guī)范要求在標(biāo)簽名中使用橫線。所以 <person>
需要寫成 <my-person>
. 如果你關(guān)心 W3C 的話就遵循這個(gè)規(guī)則. 其實(shí)兩者都能跑
2. 為什么源碼中沒有分號(hào)?
?不寫分號(hào)使代碼更整潔刹悴。這與我們的整體的最小化哲學(xué)是一致的行楞。同樣的原因,我們使用單引號(hào)土匀,也建議你在使用riot
時(shí)不要使用分號(hào)和雙引號(hào)子房。
3. 為什么使用 == 運(yùn)算符?
?運(yùn)算符本身沒有好壞之分,如果你知道它的工作原理就轧,巧妙的使用能簡(jiǎn)化你的代碼证杭,如node.nodeValue = value == null ? '' : value
這將導(dǎo)致 0
和 false
被顯示而 null
和 undefined
顯示為空字符串。這正是我們想要的妒御!
4. 使用 onclick解愤?
?onclick只是比較‘過時(shí)’。將JS和HTML放在同一個(gè)模塊里比美學(xué)重要乎莉。Riot的最小化語法使事件處理器看起來很象樣兒送讲。
?
開始學(xué)習(xí) Riot
一、自定義標(biāo)簽
1. 直觀感受
?Riot
自定義標(biāo)簽是構(gòu)建用戶界面的單元惋啃。它們構(gòu)成了應(yīng)用的”視圖”部分哼鬓。我們先從一個(gè)實(shí)現(xiàn)TODO應(yīng)用的例子來感受一下Riot的各個(gè)功能:(自定義標(biāo)簽會(huì)被 編譯 成 JavaScript.)
<todo>
<h3>{ opts.title }</h3>
<ul>
<li each={ items }>
<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.length + 1 }</button>
</form>
<script>
this.disabled = true
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 = ''
}
}
toggle(e) {
var item = e.item
item.done = !item.done
return true
}
</script>
</todo>
2. 標(biāo)簽語法
Riot標(biāo)簽是布局(HTML
)與邏輯(JavaScript
)的組合。以下是基本的規(guī)則:
- 先定義HTML边灭,然后將邏輯包含在一個(gè)可選的
<script>
標(biāo)簽里. 注意: 如果在document body
里包含標(biāo)簽定義异希,則不能使用script
標(biāo)簽,它只能用于外部標(biāo)簽文件中 - 如果不寫
<script>
標(biāo)簽绒瘦,則最后一個(gè)HTML
標(biāo)簽結(jié)束的位置被認(rèn)為是JavaScript
代碼是開始称簿。 - 自定義標(biāo)簽可以是空的,可以只有
HTML
惰帽,也可以只有JavaScript
- 標(biāo)簽屬性值的引號(hào)是可選的:
<foo bar={ baz }>
會(huì)被理解成<foo bar="{ baz }">
. - 支持ES6 方法語法:
methodName()
會(huì)被理解成this.methodName = function()
憨降,this
變量總是指向當(dāng)前標(biāo)簽實(shí)例。 - 可以使用針對(duì) class 名的簡(jiǎn)化語法: 當(dāng)
done
的值是true
時(shí)善茎,class={ completed: done }
將被渲染成class="completed"
券册。 - 如果表達(dá)式的值不為真值,則布爾型的屬性(
checked
,selected
等等..)將不被渲染:<input checked={ undefined }>
的渲染結(jié)果是<input>
. - 所有的屬性名必須是小寫. 這是由于瀏覽器的限制。
- 支持自關(guān)閉標(biāo)簽:
<div/>
等價(jià)于<div></div>
. 那些眾所周知的 “不關(guān)閉標(biāo)簽” 如<br>, <hr>, <img> or <input>
在編譯后總是不關(guān)閉的烁焙。 - 自定義標(biāo)簽需要被關(guān)閉(正常關(guān)閉航邢,或自關(guān)閉)。
- 標(biāo)準(zhǔn)
HTML tags
(label, table, a
等..) 也可以被自定義骄蝇,但并不建議這么做膳殷。
注意: 自定義標(biāo)簽文件里的標(biāo)簽定義總是從某一行的行首開始,前面不能放空格九火。內(nèi)置標(biāo)簽定義(定義在 document body
中) 必須正確縮進(jìn)赚窃,所有的自定義標(biāo)簽擁有相同的最低級(jí)的縮進(jìn)級(jí)別, 不建議將tab與空格混合使用
<!--正確-->
<t-tag>
</t-tag>
<!--正確-->
<t-tag></t-tag>
<!--錯(cuò)誤 沒從行首開始-->
<t-tag>
</t-tag>
3. 省略 script 標(biāo)簽
?可以省略 <script> 標(biāo)簽,如果這么做岔激,邏輯會(huì)認(rèn)為從最后一個(gè) HTML 標(biāo)簽結(jié)束處開始
<todo>
<!-- 布局 -->
<h3>{ opts.title }</h3>
// 邏輯
this.items = [1, 2, 3]
</todo>
4. 預(yù)處理
?可以使用 type
屬性來指定預(yù)處理器. 現(xiàn)在可選的type
值包括“coffee”, “typescript”, “es6”
和 “none”
. 也可以為 language
加上 “text/”
前綴, 如 “text/coffee”
<script type="coffee">
# coffeescript 標(biāo)簽邏輯
</script>
5. 標(biāo)簽 css
?在標(biāo)簽內(nèi)部可以放置一個(gè) style
標(biāo)簽勒极,Riot.js 會(huì)自動(dòng)將它提取出來并放到 <head>
部分,因?yàn)榉诺搅薶ead部分虑鼎,所以其他文件也能調(diào)用到該樣式
<todo>
<!-- 布局 -->
<h3>{ opts.title }</h3>
<style>
todo { display: block }
todo h3 { font-size: 120% }
/** 本標(biāo)簽其它專有的css **/
</style>
</todo>
6. 局部 CSS
?支持局部 CSS 辱匿。
<todo>
<!-- 布局 -->
<h3>{ opts.title }</h3>
<style scoped>
:scope { display: block }
h3 { font-size: 120% }
/** 本標(biāo)簽其它專有的css **/
</style>
</todo>
?css的提取和移動(dòng)只執(zhí)行一次,無論此自定義標(biāo)簽被初始化多少次炫彩。 為了能夠方便地覆蓋CSS匾七,你可以指定Riot在<head>
中的哪個(gè)位置插入標(biāo)簽所定義的css:
<style type="riot"></style>
?例如,在某些場(chǎng)景下可以指定將riot組件庫的標(biāo)簽css放在normalize.css后面江兢,而放在網(wǎng)站的整體主題CSS之前昨忆,這樣可以覆蓋組件庫的默認(rèn)風(fēng)格。
?
二杉允、自定義標(biāo)簽的加載
1. 直觀體驗(yàn)
?自定義標(biāo)簽實(shí)例被創(chuàng)建后邑贴,就可以象這樣將其加載到頁面上:
<body>
<!-- 將自定義標(biāo)簽放在body內(nèi)部的任何地方 -->
<todo></todo>
<!-- 引入 riot.js -->
<script src="riot.min.js"></script>
<!-- 引入標(biāo)簽定義文件 -->
<script src="todo.js" type="riot/tag"></script>
<!-- 加載標(biāo)簽實(shí)例 -->
<script>riot.mount('todo')</script>
</body>
?放置在頁面 body
中的自定義標(biāo)簽必須使用正常關(guān)閉方式: <todo></todo>
,自關(guān)閉的寫法: <todo/>
不支持叔磷。
2. mount 方法的使用方法
?將自定義標(biāo)簽放在 <body>
后痢缎,我們還需要調(diào)用riot.mount()
才能將其加載進(jìn)來。一個(gè)html文檔中可以包含一個(gè)自定義標(biāo)簽的多個(gè)實(shí)例世澜。
// mount 頁面中所有的自定義標(biāo)簽
riot.mount('*')
// mount 自定義標(biāo)簽到指定id的html元素
riot.mount('#my-element')
// mount 自定義標(biāo)簽到選擇器選中的html元素
riot.mount('todo, forum, comments')
3. 標(biāo)簽生命周期
自定義標(biāo)簽的創(chuàng)建過程是這樣的:
- 創(chuàng)建標(biāo)簽實(shí)例
- 標(biāo)簽定義中的
JavaScript
被執(zhí)行 - HTML 中的表達(dá)式被首次計(jì)算并首次觸發(fā)
“update”
事件 - 標(biāo)簽被加載 (
mount
) 到頁面上,觸發(fā)“mount”
事件
加載完成后署穗,表達(dá)式會(huì)在以下時(shí)機(jī)被更新:
- 當(dāng)一個(gè)事件處理器被調(diào)用(如上面ToDo示例中的
toggle
方法)后自動(dòng)更新寥裂。你也可以在事件處理器中設(shè)置e.preventUpdate = true
來禁止這種行為。 - 當(dāng)前標(biāo)簽實(shí)例的
this.update()
方法被調(diào)用時(shí) - 當(dāng)前標(biāo)簽的任何一個(gè)祖先的
this.update()
被調(diào)用時(shí). 更新從父親到兒子單向傳播案疲。 - 當(dāng)
riot.update()
方法被調(diào)用時(shí), 會(huì)更新頁面上所有的表達(dá)式封恰。
每次標(biāo)簽實(shí)例被更新,都會(huì)觸發(fā)“update”
事件褐啡。
由于表達(dá)式的首次計(jì)算發(fā)生在加載之前诺舔,所以不會(huì)有類似 <img src={ src }>
計(jì)算失敗之類的意外問題。
4. 監(jiān)聽生命周期事件
在標(biāo)簽定義內(nèi)部可以這樣監(jiān)聽各種生命周期事件:
<todo>
this.on('before-mount', function() {
// 標(biāo)簽被加載之前
})
this.on('mount', function() {
// 標(biāo)簽實(shí)例被加載到頁面上以后
})
this.on('update', function() {
// 允許在更新之前重新計(jì)算上下文數(shù)據(jù)
})
this.on('updated', function() {
// 標(biāo)簽?zāi)0甯潞? })
this.on('before-unmount', function() {
// 標(biāo)簽實(shí)例被刪除之前
})
this.on('unmount', function() {
// 標(biāo)簽實(shí)例被從頁面上刪除后
})
// 想監(jiān)聽所有事件?
this.on('all', function(eventName) {
console.info(eventName)
})
</todo>
5. 訪問 DOM 元素
?Riot 允許開發(fā)人員通過 this
實(shí)例直接訪問設(shè)置了 name
屬性的元素低飒,也提供了各種簡(jiǎn)化的屬性方法如 if="{...}"
许昨,但偶爾你還是需要直接完成這些內(nèi)置手段所不支持的DOM操作。
6. 如何使用 jQuery, Zepto, querySelector, 等等
?如果需要在Riot中訪問DOM,要注意 DOM 元素的初始化發(fā)生在第一個(gè) update()
事件被觸發(fā)之后褥赊,這意味著在這之前試圖選擇這個(gè)元素將都失敗糕档。
<example-tag>
<p id="findMe">Do I even Exist?</p>
<script>
var test1 = document.getElementById('findMe')
console.log('test1', test1) // 失敗
this.on('update', function(){
var test2 = document.getElementById('findMe')
console.log('test2', test2) // 成功
})
</script>
</example-tag>
?你可能并不打算在每次update
時(shí)都去取一下你想要的元素,而更傾向于在 mount
事件中做這件事拌喉。
<example-tag>
<p id="findMe">Do I even Exist?</p>
<script>
var test1 = document.getElementById('findMe')
console.log('test1', test1) // 失敗
this.on('update', function(){
var test2 = document.getElementById('findMe')
console.log('test2', test2) // 成功速那,每次更新都會(huì)觸發(fā)
})
this.on('mount', function(){
var test3 = document.getElementById('findMe')
console.log('test3', test3) // 成功,實(shí)例被加載以后尿背,只觸發(fā)一次
})
</script>
</example-tag>
7. 基于上下文的 DOM 查詢
?現(xiàn)在我們知道了如何在處理 update
或 mount
事件時(shí)獲取 DOM 元素端仰,現(xiàn)在我們可以利用這一點(diǎn),將 根元素 (我們所創(chuàng)建的 riot
標(biāo)簽實(shí)例) 作為DOM元素查詢的上下文田藐。
<example-tag>
<p id="findMe">Do I even Exist?</p>
<p>Is this real life?</p>
<p>Or just fantasy?</p>
<script>
this.on('mount', function(){
// Contexted jQuery
$('p', this.root)
// Contexted Query Selector
this.root.querySelectorAll('p')
})
</script>
</example-tag>
8. 標(biāo)簽選項(xiàng)(參數(shù))
?mount
方法的第二個(gè)參數(shù)用來傳遞標(biāo)簽選項(xiàng)
<script>
riot.mount('todo', { title: 'My TODO app', items: [ ... ] })
</script>
?在標(biāo)簽內(nèi)部荔烧,通過 opts
變量來訪問這些參數(shù),如下:
<my-tag>
<!-- 在HTML中訪問參數(shù) -->
<h3>{ opts.title }</h3>
// 在 JavaScript 中訪問參數(shù)
var title = opts.title
</my-tag>
9. Mixin
?Mixin 可以將公共代碼在不同標(biāo)簽之間方便地共享坞淮。
var OptsMixin = {
init: function() {
this.on('updated', function() { console.log('Updated!') })
},
getOpts: function() {
return this.opts
},
setOpts: function(opts, update) {
this.opts = opts
if(!update) {
this.update()
}
return this
}
}
<my-tag>
<h1>{ opts.title }</h1>
this.mixin(OptsMixin) // 用 mixin() 加上mixin名字來將mixin混入標(biāo)簽.
</my-tag>
?上例中茴晋,我們?yōu)樗?my-tag
標(biāo)簽實(shí)例混入了 OptsMixin
,它提供 getOpts
和 setOpts
方法. init
是個(gè)特殊方法回窘,用來在標(biāo)簽載入時(shí)對(duì)mixin
進(jìn)行初始化诺擅。 (init
方法不能混入此mixin
的標(biāo)簽中訪問)
var my_tag_instance = riot.mount('my-tag')[0]
console.log(my_tag_instance.getOpts()) // 輸出<my-tag>的所有的標(biāo)簽選項(xiàng)
?標(biāo)簽的mixin可以是 object – {'key': 'val'} var mix = new function(...)
– 混入任何其它類型的東西將報(bào)錯(cuò).
?現(xiàn)在:my-tag
定義又加入了一個(gè) getId
方法,以及OptMixin
中除init以外的所有其它方法
function IdMixin() {
this.getId = function() {
return this._id
}
}
var id_mixin_instance = new IdMixin()
<my-tag>
<h1>{ opts.title }</h1>
this.mixin(OptsMixin, id_mixin_instance)
</my-tag>
?由于定義在標(biāo)簽這個(gè)級(jí)別啡直,mixin
不僅僅擴(kuò)展了你的標(biāo)簽的功能, 也能夠在重復(fù)的界面中使用. 每次標(biāo)簽被加載時(shí)烁涌,即使是子標(biāo)簽, 標(biāo)簽實(shí)例也獲得了mixin
中的代碼功能.
10. 共享 mixin
?為了能夠在文件之間和項(xiàng)目之間共享mixin,提供了 riot.mixin
API. 你可以像這樣全局性地注冊(cè)mixin :
riot.mixin('mixinName', mixinObject)
用 mixin() 加上mixin名字來將mixin混入標(biāo)簽.
<my-tag>
<h1>{ opts.title }</h1>
this.mixin('mixinName')
</my-tag>
?
表達(dá)式
1, 直觀理解
在 HTML 中可以混合寫入表達(dá)式酒觅,用花括號(hào)括起來撮执。[ style 標(biāo)簽中的表達(dá)式將被忽略.]
{ /* 某個(gè)表達(dá)式 */ }
表達(dá)式可以放在html屬性里,也可以作為文本節(jié)點(diǎn)嵌入:
<h3 id={ /* 屬性表達(dá)式 */ }>
{ /* 嵌入表達(dá)式 */ } // 文本節(jié)點(diǎn)
</h3>
當(dāng)然舷丹,并不是什么表達(dá)式都是能嵌入抒钱,因?yàn)镽iot只支持屬性(值)表達(dá)式和嵌入的文本表達(dá)式,以下將會(huì)執(zhí)行失敗颜凯。
<input type="checkbox" { true ? 'checked' : ''}>
表達(dá)式是 100% 純 JavaScript. 一些例子:
{ title || 'Untitled' }
{ results ? 'ready' : 'loading' }
{ new Date() }
{ message.length > 140 && 'Message is too long' }
{ Math.round(rating) }
?建議的設(shè)計(jì)方法是使表達(dá)式保持最簡(jiǎn)從而使你的HTML盡量干凈谋币。如果你的表達(dá)式變得太復(fù)雜,建議你考慮將它的邏輯轉(zhuǎn)移到 “update”
事件的處理邏輯中. 例如:
<my-tag>
<!-- `val` 的值在下面的代碼中計(jì)算 .. -->
<p>{ val }</p>
// 每次更新時(shí)計(jì)算
this.on('update', function() {
this.val = some / complex * expression ^ here
})
</my-tag>
2. 布爾屬性
如果表達(dá)式的值為非真症概,則布爾屬性 (checked, selected
等..) 將不被渲染:
<input checked={ null }>
渲染為<input>
這與 W3C 有很大區(qū)別蕾额,W3C規(guī)范是只要布爾屬性存在即為true
,即使他的值為空
或者false
3. class 屬性簡(jiǎn)化寫法
Riot 為 CSS class 名稱提供了特殊語法. 看一個(gè)例子
<p class={ foo: true, bar: 0, baz: new Date(), zorro: 'a value' }></p>
該表達(dá)式最終被計(jì)算為 “foo baz zorro”.
彼城,只有表達(dá)式中為真值的屬性名會(huì)被加入到class名稱列表中. 這種用法并不限于用在計(jì)算class名稱的場(chǎng)合诅蝶。
4. 轉(zhuǎn)義
用以下的寫法來對(duì)花括號(hào)進(jìn)行轉(zhuǎn)義:
\\{ this is not evaluated \\} 輸出為 { this is not evaluated }
5. 渲染原始HTML
Riot 表達(dá)式只能渲染不帶HTML格式的文本值退个。如果真的需要,可以寫一個(gè)自定義標(biāo)簽來做這件事. 例如:
<raw>
<span></span>
this.root.innerHTML = opts.content
</raw>
這個(gè)標(biāo)簽定義以后调炬,可以被用在其它的標(biāo)簽里. 例如
<my-tag>
<p>原始HTML: <raw content="{ html }"/> </p>
this.html = 'Hello, <strong>world!</strong>'
</my-tag>
?
嵌套標(biāo)簽
我們來定義一個(gè)父標(biāo)簽 <account>
语盈,其中嵌套一個(gè)子標(biāo)簽 <subscription>
:
<account>
<subscription plan={ opts.plan } show_details="true" />
</account>
// 子標(biāo)簽
<subscription>
<h3>{ opts.plan.name }</h3>
// 取得標(biāo)簽選項(xiàng)
var plan = opts.plan,
show_details = opts.show_details // 取出子標(biāo)簽的標(biāo)簽屬性
// 訪問父標(biāo)簽實(shí)例
var parent = this.parent // 獲取父標(biāo)簽的標(biāo)簽實(shí)例
</subscription>
注意: 我們使用下劃線的風(fēng)格(而不是駝峰風(fēng)格)對(duì) show_details 進(jìn)行命名,由于瀏覽器的約定筐眷,駝峰風(fēng)格的命名會(huì)被自動(dòng)轉(zhuǎn)換成小寫.
如果在頁面上加載 account
標(biāo)簽黎烈,帶 plan
選項(xiàng),調(diào)用riot.mount()
方法匀谣。
<body>
<account></account>
</body>
<script>
riot.mount('account', { plan: { name: 'small', term: 'monthly' } })
</script>
注意: 嵌套的標(biāo)簽只能定義在自定義父標(biāo)簽里照棋,如果定義在頁面上,將不會(huì)被初始化武翎。
?
嵌套 HTML
“HTML transclusion”
是處理頁面上標(biāo)簽內(nèi)部 HTML 的方法. 通過內(nèi)置的 <yield>
標(biāo)簽來實(shí)現(xiàn).
<my-tag>
<p>Hello <yield/></p>
this.text = 'world'
</my-tag>
頁面上放置自定義標(biāo)簽烈炭,并包含嵌套的 HTML
<my-tag>
<b>{ text }</b>
</my-tag>
結(jié)果得到
<my-tag>
<p>Hello <b>world</b><p>
</my-tag>
?
DOM元素與name自動(dòng)綁定
?我感覺這個(gè)功能真的是帥炸了。當(dāng)html被定義好了之后宝恶,帶有 ref
屬性的DOM元素將自動(dòng)被綁定到上下文中符隙,這樣就可以從JavaScript中方便地訪問它們:
<login>
<form ref="login" onsubmit={ submit }>
<input ref="username">
<input ref="password">
<button ref="submit">
</form>
// 獲取 HTML 元素
var form = this.refs.login,
username = this.refs.username.value,
password = this.refs.password.value,
button = this.refs.submit
</login>
當(dāng)然,因?yàn)镈OM已經(jīng)被綁定到上下文中垫毙,所以我們也可以直接在HTML中以表達(dá)式形式引用:
<div>{ username.value }</div>
?
事件處理器
1. 一般處理
響應(yīng)DOM事件的函數(shù)稱為 “事件處理器”.
<login>
<form onsubmit={ submit }>
</form>
// 上面的表單提交時(shí)調(diào)用此方法
submit(e) {
}
</login>
?以”on”
(onclick, onsubmit, oninput
等…)開頭的屬性的值是一個(gè)函數(shù)名霹疫,當(dāng)相應(yīng)的事件發(fā)生時(shí),此函數(shù)被調(diào)用. 函數(shù)也可以通過表達(dá)式來動(dòng)態(tài)定義:
<form onsubmit={ condition ? method_a : method_b }>
?在此函數(shù)中综芥,this
指向當(dāng)前標(biāo)簽實(shí)例丽蝎。當(dāng)處理器被調(diào)用之后, this.update()
將被自動(dòng)調(diào)用膀藐,將所有可能的變化體現(xiàn)到 UI 上屠阻。
2. 阻止默認(rèn)行為
?如果事件的目標(biāo)元素不是checkbox
或radio
按鈕,默認(rèn)的事件處理器行為是 自動(dòng)取消事件 . 意思是它總會(huì)自動(dòng)調(diào)用 e.preventDefault()
, 因?yàn)橥ǔ6夹枰{(diào)用它额各,而且容易被遺忘国觉。如果要讓瀏覽器執(zhí)行默認(rèn)的操作,在處理器中返回 true
就可以了.
submit() {
return true
}
3. 事件對(duì)象
?事件處理器的第一個(gè)參數(shù)是標(biāo)準(zhǔn)的事件對(duì)象虾啦。事件對(duì)象的以下屬性已經(jīng)被Riot進(jìn)行了跨瀏覽器兼容
-
e.currentTarget
指向事件處理器的所屬元素. -
e.target
是發(fā)起事件的元素麻诀,與currentTarget
不一定相同. -
e.which
是鍵盤事件(keypress, keyup
, 等…)中的鍵值 . -
e.item
是 循環(huán) 中的當(dāng)前元素.
?
渲染條件 - show / hide / if
可以基于條件來決定顯示或隱藏元素。例如:
<div if={ is_premium }>
<p>This is for premium users only</p>
</div>
同樣, 渲染條件中的表達(dá)式也可以是一個(gè)簡(jiǎn)單屬性傲醉,或一個(gè)完整的 JavaScript 表達(dá)式. 有以下選擇:
- show – 當(dāng)值為真時(shí)用 style="display: ''" 顯示元素
- hide – 當(dāng)值為真時(shí)用 style="display: none" 隱藏元素
- if – 在 document 中添加 (真值) 或刪除 (假值) 元素
判斷用的操作符是 ==
而非 ===
. 例如: 'a string' == true
.
?
循環(huán)
1. 循環(huán)是用 each 屬性來實(shí)現(xiàn):
<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
屬性的html元素根據(jù)對(duì)數(shù)組中的所有項(xiàng)進(jìn)行重復(fù)针饥。 當(dāng)數(shù)組使用 push()
, slice()
或 splice
方法進(jìn)行操作后,新的元素將被自動(dòng)添加或刪除需频。
2. 上下文
?循環(huán)中的每一項(xiàng)將創(chuàng)建一個(gè)新的上下文(標(biāo)簽實(shí)例);如果有嵌套的循環(huán)筷凤,循環(huán)中的子標(biāo)簽都會(huì)繼承父循環(huán)中定義了而自己未定義的屬性和方法昭殉。Riot通過這種方法來避免重寫不應(yīng)在父標(biāo)簽中重寫的東西苞七。
?從子上下文中可以通過顯式地調(diào)用 parent
變量來訪問上級(jí)上下文.
<todo>
<div each={ items }>
<h3>{ title }</h3>
<a onclick={ parent.remove }>Remove</a>
</div>
this.items = [ { title: 'First' }, { title: 'Second' } ]
remove(event) {
}
</todo>
?該例中,除了 each 屬性外挪丢,其它都屬于子上下文, 因此 title
可以被直接訪問而 remove
需要從 parent.
中訪問蹂风,因?yàn)?code>remove方法并不是循環(huán)元素的屬性.
?每一個(gè)循環(huán)項(xiàng)都是一個(gè)標(biāo)簽實(shí)例. Riot 不會(huì)修改原始數(shù)據(jù)項(xiàng),因此不會(huì)為其添加新的屬性乾蓬。
3. 循環(huán)項(xiàng)的事件處理器
事件處理器中可以通過 event.item
來訪問單個(gè)集合項(xiàng)惠啄。這種辦法采用了事件委托機(jī)制,極大減少了對(duì)DOM的訪問任内。
下面我們來實(shí)現(xiàn)上方的 remove
函數(shù):
<todo>
<div each={ items }>
<h3>{ title }</h3>
<a onclick={ parent.remove }>Remove</a>
</div>
this.items = [ { title: 'First' }, { title: 'Second' } ]
remove(event) {
// 循環(huán)項(xiàng)
var item = event.item
// 在集合中的索引
var index = this.items.indexOf(item)
// 從集合中刪除
this.items.splice(index, 1)
}
</todo>
?事件處理器被執(zhí)行后撵渡,當(dāng)前標(biāo)簽實(shí)例會(huì)自動(dòng)調(diào)用 this.update()
(你也可以在事件處理器中設(shè)置 e.preventUpdate = true
來禁止這種行為)從而導(dǎo)致所有循環(huán)項(xiàng)也被更新. 父親會(huì)發(fā)現(xiàn)集合中被刪除了一項(xiàng),從而將對(duì)應(yīng)的DOM結(jié)點(diǎn)從document
中刪除死嗦。
4. 循環(huán)自定義標(biāo)簽
自定義標(biāo)簽也可以被循環(huán)
<todo-item each="{ items }" data="{ this }"></todo-item>
當(dāng)前循環(huán)項(xiàng)可以用 this
來引用趋距,你可以用它來將循環(huán)項(xiàng)作為一個(gè)參數(shù)傳遞給循環(huán)標(biāo)簽。
5. 非對(duì)象數(shù)組
數(shù)組元素不要求是對(duì)象. 也可以是字符串或數(shù)字. 這時(shí)可以用 { 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
是索引. 這兩個(gè)變量的變量名可以自由選擇节腐。
6. 對(duì)象循環(huán)
也可以對(duì)普通對(duì)象做循環(huán). 例如:
<my-tag>
<p each="{ name, value in obj }">{ name } = { value }</p>
this.obj = {
key1: 'value1',
key2: 1110.8900,
key3: Math.random()
}
</my-tag>
?不太建議使用對(duì)象循環(huán),因?yàn)樵趦?nèi)部實(shí)現(xiàn)中摘盆,Riot使用 JSON.stringify
來探測(cè)對(duì)象內(nèi)容的改變. 整個(gè) 對(duì)象都會(huì)被檢查翼雀,只要有一處改變,整個(gè)循環(huán)將會(huì)被重新渲染. 會(huì)很慢. 普通的數(shù)組要快得多孩擂,而且只有變化的部分會(huì)在頁面上體現(xiàn)狼渊。
7. 循環(huán)的高級(jí)技巧
?在 riot v2.3 中,為了使循環(huán)渲染更可靠肋殴,DOM 結(jié)點(diǎn)的移動(dòng)囤锉,插入和刪除總是與數(shù)據(jù)集合同步的: 這種策略會(huì)導(dǎo)致渲染過程比之前的版本慢一些。要使用更快的渲染算法护锤,可以在循環(huán)結(jié)點(diǎn)上加上 no-reorder 屬性官地。
<loop>
<div each="{ item in items }" no-reorder>{ item }</div>
</loop>
?
使用標(biāo)準(zhǔn) HTML 元素作為標(biāo)簽 | #riot-tag
頁面body
中的標(biāo)準(zhǔn) HTML 元素也可以作為riot標(biāo)簽來使用,只要加上 riot-tag
屬性.
<ul riot-tag="my-tag"></ul>
這為用戶提供了一種選擇烙懦,與css框架的兼容性更好. 這些標(biāo)簽將被與其它自定義標(biāo)簽一樣進(jìn)行處理驱入。
riot.mount('my-tag')
會(huì)將 my-tag
標(biāo)簽 加載到 ul
元素上
?
服務(wù)端渲染 | #server-side
Riot 支持服務(wù)端渲染,使用 Node/io.js 可以方便地引用標(biāo)簽定義并渲染成 html:
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>
循環(huán)和條件渲染都支持.