前端,你是文藝界的程序員
我為什么要用Riot
?優點明顯,體積小,加載快,繼承了react,Polymer等框架的優勢,并簡化他們,主要從以下幾個方面考慮:
1. 自定義標簽。
?Riot 在所有瀏覽器上支持自定義標簽,我們能將頁面組件化,一個自定義標簽結構如下所示:
<todo>
<!--布局-->
<h3>{ item }</h3>
<!-- css -->
<style scoped>
h3 { font-size: 14px; }
</style>
<!-- 業務邏輯 -->
<script>
var self = this;
self.item = ‘Riot sample’;
</script>
</todo>
2. 對閱讀友好
?有了自定義標簽功能后,我們可以用很簡潔語言‘拼湊’出復雜的用戶界面,加上語義化標簽定義,在閱讀的時候,很容易看清楚哪個標簽是給html加入了什么組件,一共有多少個組件,這樣一個頁面大致實現什么功能,甚至不用看瀏覽器展示都能明白。你的代碼可能是這樣的
<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>
?很清楚,頁面被分成四三個模塊,一個標題h1
,一個header
,一個content
和一個footer
,這樣就在我們腦海構成一個基礎的網頁模型
?Riot 標簽首先被 編譯 成標準 JavaScript,然后在瀏覽器中運行。
3. 虛擬 DOM
- 保證最少量的DOM 更新和數據流動
- 單向數據流: 更新和刪除操作由父組件向子組件傳播
- 表達式會進行預編譯和緩存以保證性能
- 為更精細的控制提供生命周期事件
- 支持自定義標簽的服務端渲染,支持單語言應用
4. 與標準保持一致
- 沒有專有的事件系統
- 渲染出的 DOM 節點可以放心地用其它工具(庫)進行操作
- 不要求額外的 HTML 根元素或
data-
屬性 - 與 jQuery 友好共存
5. 友好的語法
(1).強大的簡寫語法
class={ enabled: is_enabled, hidden: hasError() }
(2).語義化強,不需要費腦記憶
render, state, constructor 或 shouldComponentUpdate
(3).直接插值
Add #{ items.length + 1 } 或 class="item { selected: flag }"
(4).用 <script>
標簽來包含邏輯代碼不是必需的
(5).緊湊的 ES6
方法定義語法
6. 麻雀小,五臟全
最小化是 Riot 區別于其它庫的重要特點,它所提供的 API 方法比其他庫要少 10 至 100倍。
?react.min.js – 34.89KB
?polymer.min.js – 49.38KB
?riot.min.js – 10.38KB
Riot 擁有創建現代客戶端應用的所有必需的成分:
- “響應式” 視圖層用來創建用戶界面
- 用來在各獨立模塊之間進行通信的事件庫
- 用來管理
URL
和瀏覽器回退按鈕的路由器(Router
)
?
學前三兩個FAQ
1. 標簽名中必須使用橫線 (-) 嗎?
?W3C 規范要求在標簽名中使用橫線。所以 <person>
需要寫成 <my-person>
. 如果你關心 W3C 的話就遵循這個規則. 其實兩者都能跑
2. 為什么源碼中沒有分號?
?不寫分號使代碼更整潔。這與我們的整體的最小化哲學是一致的。同樣的原因,我們使用單引號,也建議你在使用riot
時不要使用分號和雙引號。
3. 為什么使用 == 運算符?
?運算符本身沒有好壞之分,如果你知道它的工作原理,巧妙的使用能簡化你的代碼,如node.nodeValue = value == null ? '' : value
這將導致 0
和 false
被顯示而 null
和 undefined
顯示為空字符串。這正是我們想要的!
4. 使用 onclick?
?onclick只是比較‘過時’。將JS和HTML放在同一個模塊里比美學重要。Riot的最小化語法使事件處理器看起來很象樣兒。
?
開始學習 Riot
一、自定義標簽
1. 直觀感受
?Riot
自定義標簽是構建用戶界面的單元。它們構成了應用的”視圖”部分。我們先從一個實現TODO應用的例子來感受一下Riot的各個功能:(自定義標簽會被 編譯 成 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. 標簽語法
Riot標簽是布局(HTML
)與邏輯(JavaScript
)的組合。以下是基本的規則:
- 先定義HTML,然后將邏輯包含在一個可選的
<script>
標簽里. 注意: 如果在document body
里包含標簽定義,則不能使用script
標簽,它只能用于外部標簽文件中 - 如果不寫
<script>
標簽,則最后一個HTML
標簽結束的位置被認為是JavaScript
代碼是開始。 - 自定義標簽可以是空的,可以只有
HTML
,也可以只有JavaScript
- 標簽屬性值的引號是可選的:
<foo bar={ baz }>
會被理解成<foo bar="{ baz }">
. - 支持ES6 方法語法:
methodName()
會被理解成this.methodName = function()
,this
變量總是指向當前標簽實例。 - 可以使用針對 class 名的簡化語法: 當
done
的值是true
時,class={ completed: done }
將被渲染成class="completed"
。 - 如果表達式的值不為真值,則布爾型的屬性(
checked
,selected
等等..)將不被渲染:<input checked={ undefined }>
的渲染結果是<input>
. - 所有的屬性名必須是小寫. 這是由于瀏覽器的限制。
- 支持自關閉標簽:
<div/>
等價于<div></div>
. 那些眾所周知的 “不關閉標簽” 如<br>, <hr>, <img> or <input>
在編譯后總是不關閉的。 - 自定義標簽需要被關閉(正常關閉,或自關閉)。
- 標準
HTML tags
(label, table, a
等..) 也可以被自定義,但并不建議這么做。
注意: 自定義標簽文件里的標簽定義總是從某一行的行首開始,前面不能放空格。內置標簽定義(定義在 document body
中) 必須正確縮進,所有的自定義標簽擁有相同的最低級的縮進級別, 不建議將tab與空格混合使用
<!--正確-->
<t-tag>
</t-tag>
<!--正確-->
<t-tag></t-tag>
<!--錯誤 沒從行首開始-->
<t-tag>
</t-tag>
3. 省略 script 標簽
?可以省略 <script> 標簽,如果這么做,邏輯會認為從最后一個 HTML 標簽結束處開始
<todo>
<!-- 布局 -->
<h3>{ opts.title }</h3>
// 邏輯
this.items = [1, 2, 3]
</todo>
4. 預處理
?可以使用 type
屬性來指定預處理器. 現在可選的type
值包括“coffee”, “typescript”, “es6”
和 “none”
. 也可以為 language
加上 “text/”
前綴, 如 “text/coffee”
<script type="coffee">
# coffeescript 標簽邏輯
</script>
5. 標簽 css
?在標簽內部可以放置一個 style
標簽,Riot.js 會自動將它提取出來并放到 <head>
部分,因為放到了head部分,所以其他文件也能調用到該樣式
<todo>
<!-- 布局 -->
<h3>{ opts.title }</h3>
<style>
todo { display: block }
todo h3 { font-size: 120% }
/** 本標簽其它專有的css **/
</style>
</todo>
6. 局部 CSS
?支持局部 CSS 。
<todo>
<!-- 布局 -->
<h3>{ opts.title }</h3>
<style scoped>
:scope { display: block }
h3 { font-size: 120% }
/** 本標簽其它專有的css **/
</style>
</todo>
?css的提取和移動只執行一次,無論此自定義標簽被初始化多少次。 為了能夠方便地覆蓋CSS,你可以指定Riot在<head>
中的哪個位置插入標簽所定義的css:
<style type="riot"></style>
?例如,在某些場景下可以指定將riot組件庫的標簽css放在normalize.css后面,而放在網站的整體主題CSS之前,這樣可以覆蓋組件庫的默認風格。
?
二、自定義標簽的加載
1. 直觀體驗
?自定義標簽實例被創建后,就可以象這樣將其加載到頁面上:
<body>
<!-- 將自定義標簽放在body內部的任何地方 -->
<todo></todo>
<!-- 引入 riot.js -->
<script src="riot.min.js"></script>
<!-- 引入標簽定義文件 -->
<script src="todo.js" type="riot/tag"></script>
<!-- 加載標簽實例 -->
<script>riot.mount('todo')</script>
</body>
?放置在頁面 body
中的自定義標簽必須使用正常關閉方式: <todo></todo>
,自關閉的寫法: <todo/>
不支持。
2. mount 方法的使用方法
?將自定義標簽放在 <body>
后,我們還需要調用riot.mount()
才能將其加載進來。一個html文檔中可以包含一個自定義標簽的多個實例。
// mount 頁面中所有的自定義標簽
riot.mount('*')
// mount 自定義標簽到指定id的html元素
riot.mount('#my-element')
// mount 自定義標簽到選擇器選中的html元素
riot.mount('todo, forum, comments')
3. 標簽生命周期
自定義標簽的創建過程是這樣的:
- 創建標簽實例
- 標簽定義中的
JavaScript
被執行 - HTML 中的表達式被首次計算并首次觸發
“update”
事件 - 標簽被加載 (
mount
) 到頁面上,觸發“mount”
事件
加載完成后,表達式會在以下時機被更新:
- 當一個事件處理器被調用(如上面ToDo示例中的
toggle
方法)后自動更新。你也可以在事件處理器中設置e.preventUpdate = true
來禁止這種行為。 - 當前標簽實例的
this.update()
方法被調用時 - 當前標簽的任何一個祖先的
this.update()
被調用時. 更新從父親到兒子單向傳播。 - 當
riot.update()
方法被調用時, 會更新頁面上所有的表達式。
每次標簽實例被更新,都會觸發“update”
事件。
由于表達式的首次計算發生在加載之前,所以不會有類似 <img src={ src }>
計算失敗之類的意外問題。
4. 監聽生命周期事件
在標簽定義內部可以這樣監聽各種生命周期事件:
<todo>
this.on('before-mount', function() {
// 標簽被加載之前
})
this.on('mount', function() {
// 標簽實例被加載到頁面上以后
})
this.on('update', function() {
// 允許在更新之前重新計算上下文數據
})
this.on('updated', function() {
// 標簽模板更新后
})
this.on('before-unmount', function() {
// 標簽實例被刪除之前
})
this.on('unmount', function() {
// 標簽實例被從頁面上刪除后
})
// 想監聽所有事件?
this.on('all', function(eventName) {
console.info(eventName)
})
</todo>
5. 訪問 DOM 元素
?Riot 允許開發人員通過 this
實例直接訪問設置了 name
屬性的元素,也提供了各種簡化的屬性方法如 if="{...}"
,但偶爾你還是需要直接完成這些內置手段所不支持的DOM操作。
6. 如何使用 jQuery, Zepto, querySelector, 等等
?如果需要在Riot中訪問DOM,要注意 DOM 元素的初始化發生在第一個 update()
事件被觸發之后,這意味著在這之前試圖選擇這個元素將都失敗。
<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
時都去取一下你想要的元素,而更傾向于在 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) // 成功,每次更新都會觸發
})
this.on('mount', function(){
var test3 = document.getElementById('findMe')
console.log('test3', test3) // 成功,實例被加載以后,只觸發一次
})
</script>
</example-tag>
7. 基于上下文的 DOM 查詢
?現在我們知道了如何在處理 update
或 mount
事件時獲取 DOM 元素,現在我們可以利用這一點,將 根元素 (我們所創建的 riot
標簽實例) 作為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. 標簽選項(參數)
?mount
方法的第二個參數用來傳遞標簽選項
<script>
riot.mount('todo', { title: 'My TODO app', items: [ ... ] })
</script>
?在標簽內部,通過 opts
變量來訪問這些參數,如下:
<my-tag>
<!-- 在HTML中訪問參數 -->
<h3>{ opts.title }</h3>
// 在 JavaScript 中訪問參數
var title = opts.title
</my-tag>
9. Mixin
?Mixin 可以將公共代碼在不同標簽之間方便地共享。
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混入標簽.
</my-tag>
?上例中,我們為所有 my-tag
標簽實例混入了 OptsMixin
,它提供 getOpts
和 setOpts
方法. init
是個特殊方法,用來在標簽載入時對mixin
進行初始化。 (init
方法不能混入此mixin
的標簽中訪問)
var my_tag_instance = riot.mount('my-tag')[0]
console.log(my_tag_instance.getOpts()) // 輸出<my-tag>的所有的標簽選項
?標簽的mixin可以是 object – {'key': 'val'} var mix = new function(...)
– 混入任何其它類型的東西將報錯.
?現在:my-tag
定義又加入了一個 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>
?由于定義在標簽這個級別,mixin
不僅僅擴展了你的標簽的功能, 也能夠在重復的界面中使用. 每次標簽被加載時,即使是子標簽, 標簽實例也獲得了mixin
中的代碼功能.
10. 共享 mixin
?為了能夠在文件之間和項目之間共享mixin,提供了 riot.mixin
API. 你可以像這樣全局性地注冊mixin :
riot.mixin('mixinName', mixinObject)
用 mixin() 加上mixin名字來將mixin混入標簽.
<my-tag>
<h1>{ opts.title }</h1>
this.mixin('mixinName')
</my-tag>
?
表達式
1, 直觀理解
在 HTML 中可以混合寫入表達式,用花括號括起來。[ style 標簽中的表達式將被忽略.]
{ /* 某個表達式 */ }
表達式可以放在html屬性里,也可以作為文本節點嵌入:
<h3 id={ /* 屬性表達式 */ }>
{ /* 嵌入表達式 */ } // 文本節點
</h3>
當然,并不是什么表達式都是能嵌入,因為Riot只支持屬性(值)表達式和嵌入的文本表達式,以下將會執行失敗。
<input type="checkbox" { true ? 'checked' : ''}>
表達式是 100% 純 JavaScript. 一些例子:
{ title || 'Untitled' }
{ results ? 'ready' : 'loading' }
{ new Date() }
{ message.length > 140 && 'Message is too long' }
{ Math.round(rating) }
?建議的設計方法是使表達式保持最簡從而使你的HTML盡量干凈。如果你的表達式變得太復雜,建議你考慮將它的邏輯轉移到 “update”
事件的處理邏輯中. 例如:
<my-tag>
<!-- `val` 的值在下面的代碼中計算 .. -->
<p>{ val }</p>
// 每次更新時計算
this.on('update', function() {
this.val = some / complex * expression ^ here
})
</my-tag>
2. 布爾屬性
如果表達式的值為非真,則布爾屬性 (checked, selected
等..) 將不被渲染:
<input checked={ null }>
渲染為<input>
這與 W3C 有很大區別,W3C規范是只要布爾屬性存在即為true
,即使他的值為空
或者false
3. class 屬性簡化寫法
Riot 為 CSS class 名稱提供了特殊語法. 看一個例子
<p class={ foo: true, bar: 0, baz: new Date(), zorro: 'a value' }></p>
該表達式最終被計算為 “foo baz zorro”.
,只有表達式中為真值的屬性名會被加入到class名稱列表中. 這種用法并不限于用在計算class名稱的場合。
4. 轉義
用以下的寫法來對花括號進行轉義:
\\{ this is not evaluated \\} 輸出為 { this is not evaluated }
5. 渲染原始HTML
Riot 表達式只能渲染不帶HTML格式的文本值。如果真的需要,可以寫一個自定義標簽來做這件事. 例如:
<raw>
<span></span>
this.root.innerHTML = opts.content
</raw>
這個標簽定義以后,可以被用在其它的標簽里. 例如
<my-tag>
<p>原始HTML: <raw content="{ html }"/> </p>
this.html = 'Hello, <strong>world!</strong>'
</my-tag>
?
嵌套標簽
我們來定義一個父標簽 <account>
,其中嵌套一個子標簽 <subscription>
:
<account>
<subscription plan={ opts.plan } show_details="true" />
</account>
// 子標簽
<subscription>
<h3>{ opts.plan.name }</h3>
// 取得標簽選項
var plan = opts.plan,
show_details = opts.show_details // 取出子標簽的標簽屬性
// 訪問父標簽實例
var parent = this.parent // 獲取父標簽的標簽實例
</subscription>
注意: 我們使用下劃線的風格(而不是駝峰風格)對 show_details 進行命名,由于瀏覽器的約定,駝峰風格的命名會被自動轉換成小寫.
如果在頁面上加載 account
標簽,帶 plan
選項,調用riot.mount()
方法。
<body>
<account></account>
</body>
<script>
riot.mount('account', { plan: { name: 'small', term: 'monthly' } })
</script>
注意: 嵌套的標簽只能定義在自定義父標簽里,如果定義在頁面上,將不會被初始化。
?
嵌套 HTML
“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>
?
DOM元素與name自動綁定
?我感覺這個功能真的是帥炸了。當html被定義好了之后,帶有 ref
屬性的DOM元素將自動被綁定到上下文中,這樣就可以從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>
當然,因為DOM已經被綁定到上下文中,所以我們也可以直接在HTML中以表達式形式引用:
<div>{ username.value }</div>
?
事件處理器
1. 一般處理
響應DOM事件的函數稱為 “事件處理器”.
<login>
<form onsubmit={ submit }>
</form>
// 上面的表單提交時調用此方法
submit(e) {
}
</login>
?以”on”
(onclick, onsubmit, oninput
等…)開頭的屬性的值是一個函數名,當相應的事件發生時,此函數被調用. 函數也可以通過表達式來動態定義:
<form onsubmit={ condition ? method_a : method_b }>
?在此函數中,this
指向當前標簽實例。當處理器被調用之后, this.update()
將被自動調用,將所有可能的變化體現到 UI 上。
2. 阻止默認行為
?如果事件的目標元素不是checkbox
或radio
按鈕,默認的事件處理器行為是 自動取消事件 . 意思是它總會自動調用 e.preventDefault()
, 因為通常都需要調用它,而且容易被遺忘。如果要讓瀏覽器執行默認的操作,在處理器中返回 true
就可以了.
submit() {
return true
}
3. 事件對象
?事件處理器的第一個參數是標準的事件對象。事件對象的以下屬性已經被Riot進行了跨瀏覽器兼容
-
e.currentTarget
指向事件處理器的所屬元素. -
e.target
是發起事件的元素,與currentTarget
不一定相同. -
e.which
是鍵盤事件(keypress, keyup
, 等…)中的鍵值 . -
e.item
是 循環 中的當前元素.
?
渲染條件 - show / hide / if
可以基于條件來決定顯示或隱藏元素。例如:
<div if={ is_premium }>
<p>This is for premium users only</p>
</div>
同樣, 渲染條件中的表達式也可以是一個簡單屬性,或一個完整的 JavaScript 表達式. 有以下選擇:
- show – 當值為真時用 style="display: ''" 顯示元素
- hide – 當值為真時用 style="display: none" 隱藏元素
- if – 在 document 中添加 (真值) 或刪除 (假值) 元素
判斷用的操作符是 ==
而非 ===
. 例如: 'a string' == true
.
?
循環
1. 循環是用 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
屬性的html元素根據對數組中的所有項進行重復。 當數組使用 push()
, slice()
或 splice
方法進行操作后,新的元素將被自動添加或刪除。
2. 上下文
?循環中的每一項將創建一個新的上下文(標簽實例);如果有嵌套的循環,循環中的子標簽都會繼承父循環中定義了而自己未定義的屬性和方法。Riot通過這種方法來避免重寫不應在父標簽中重寫的東西。
?從子上下文中可以通過顯式地調用 parent
變量來訪問上級上下文.
<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.
中訪問,因為remove
方法并不是循環元素的屬性.
?每一個循環項都是一個標簽實例. Riot 不會修改原始數據項,因此不會為其添加新的屬性。
3. 循環項的事件處理器
事件處理器中可以通過 event.item
來訪問單個集合項。這種辦法采用了事件委托機制,極大減少了對DOM的訪問。
下面我們來實現上方的 remove
函數:
<todo>
<div each={ items }>
<h3>{ title }</h3>
<a onclick={ parent.remove }>Remove</a>
</div>
this.items = [ { title: 'First' }, { title: 'Second' } ]
remove(event) {
// 循環項
var item = event.item
// 在集合中的索引
var index = this.items.indexOf(item)
// 從集合中刪除
this.items.splice(index, 1)
}
</todo>
?事件處理器被執行后,當前標簽實例會自動調用 this.update()
(你也可以在事件處理器中設置 e.preventUpdate = true
來禁止這種行為)從而導致所有循環項也被更新. 父親會發現集合中被刪除了一項,從而將對應的DOM結點從document
中刪除。
4. 循環自定義標簽
自定義標簽也可以被循環
<todo-item each="{ items }" data="{ this }"></todo-item>
當前循環項可以用 this
來引用,你可以用它來將循環項作為一個參數傳遞給循環標簽。
5. 非對象數組
數組元素不要求是對象. 也可以是字符串或數字. 這時可以用 { 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
是索引. 這兩個變量的變量名可以自由選擇。
6. 對象循環
也可以對普通對象做循環. 例如:
<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
來探測對象內容的改變. 整個 對象都會被檢查,只要有一處改變,整個循環將會被重新渲染. 會很慢. 普通的數組要快得多,而且只有變化的部分會在頁面上體現。
7. 循環的高級技巧
?在 riot v2.3 中,為了使循環渲染更可靠,DOM 結點的移動,插入和刪除總是與數據集合同步的: 這種策略會導致渲染過程比之前的版本慢一些。要使用更快的渲染算法,可以在循環結點上加上 no-reorder 屬性。
<loop>
<div each="{ item in items }" no-reorder>{ item }</div>
</loop>
?
使用標準 HTML 元素作為標簽 | #riot-tag
頁面body
中的標準 HTML 元素也可以作為riot標簽來使用,只要加上 riot-tag
屬性.
<ul riot-tag="my-tag"></ul>
這為用戶提供了一種選擇,與css框架的兼容性更好. 這些標簽將被與其它自定義標簽一樣進行處理。
riot.mount('my-tag')
會將 my-tag
標簽 加載到 ul
元素上
?
服務端渲染 | #server-side
Riot 支持服務端渲染,使用 Node/io.js 可以方便地引用標簽定義并渲染成 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>
循環和條件渲染都支持.