avalon1.5一個重要技術升級是引進異步渲染。異步渲染在遊戲界有一個更專業的名字,叫雙緩衝。遊戲界要刷新界面與咱們刷新瀏覽器視圖,面臨的問題是一致的。視圖是由許多存在套嵌關係的方塊組成,它們每個的改動,均可能引發reflow(其父節點,其父父節點的大小從新計算),這是形成性能問題的關鍵。javascript
雙緩衝技術的主要原理是:當一個動畫爭先顯示時,程序又在改變它,前面的畫面還沒顯示完,程序又要求從新繪製,這樣屏幕就會不停閃爍。爲了不閃爍,可使用雙緩衝技術,將要處理的圖片都放在內存中處理好事後,再將其顯示到屏幕上。這樣出來的就是完整的圖像,不會出現閃爍現象。html
MVVM框架帶來一個革命性的優化是,用戶只操做VM就好了,視圖由框架來同步更新。所以這個同步過程,咱們就能夠加入優化。好比說angular,就是靠用戶手動調用$apply來驅動髒檢測,只有數據不一致的地方,纔會操做DOM。因而沒有了aaa.innerHTML = "xxx&"; aaa.innerHTML = "xxx"的愚蠢代碼。java
可能用戶會說我怎麼可能會這樣寫呢,那是你由於用於jquery,看到它是在同一個循環中對某個節點執行屢次相同的操做。node
再回過頭來看avalon。avalon是基於事件驅動(經過Object.defineProperty劫持了對象的屬性),當用戶修改了某屬性,就會當即同步視圖。這種機制最大的好處是,方便與jQuery或其餘操做DOM的庫配合使用jquery
avalon.config.async = false var vm = avalon.define({ $id: "test", a: 1 }) setTimeout(function(){ vm.a = 2 alert(document.getElementById("aaa").innerHTML) //2 }, 1000)
{{a}}// 2
壞處是可能會形成性能問題。做爲測試,咱們在avalon的text指令加入這樣一行日誌: git
avalon.directive("text", { update: function (value) { console.log(value) var elem = this.element value = value == null ? "" : value //不在頁面上顯示undefined null if (elem.nodeType === 3) { //綁定在文本節點上 try { //IE對遊離於DOM樹外的節點賦值會報錯 elem.data = value } catch (e) { } } else { //綁定在特性節點上 if ("textContent" in elem) { elem.textContent = value } else { elem.innerText = value } } } })
` 測試頁面爲:github
<html> <head> <title>TODO supply a title</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="avalon.js"></script> <script> avalon.config.async = false var vm = avalon.define({ $id: "test", a: 1 }) setTimeout(function () { vm.a = 2 vm.a = 3 vm.a = 4 vm.a = 5 vm.a = 6 }) </script> </head> <body> <div ms-controller="test"> <div id="aaa">{{a}}</div> </div> </body> </html>
在avalon1.5中,打開 avalon.config.async = true開關後,就只輸出兩次。第一次掃描確定當即出來,之後就是異步了。數組
avalon作到這個很是簡單,由於通過屢次重構優化,從VM到V的同步,都要通過notifySubscribers這個方法。它是用來執行當前屬性對應的訂閱數組中的每一個對象的update方法。瀏覽器
function notifySubscribers(subs) {
if (!subs)
return
if (new Date() - beginTime > 444 && typeof subs[0] === "object") {
rejectDisposeQueue() //若是這個綁定對象的節點已經被移出DOM樹,那麼須要對這sub對象進行GC回收處理
}
for (var i = 0, sub; sub = subs[i++]; ) {
sub.update && sub.update() //最小化刷新DOM樹
}
}
` 如今改爲這樣了app
function notifySubscribers(subs) {
if (!subs)
return
if (new Date() - beginTime > 444 && typeof subs[0] === "object") {
rejectDisposeQueue()//...
}
if (kernel.async) {
buffer.render()
for (var i = 0, sub; sub = subs[i++]; ) {
if (sub.update) {
avalon.Array.ensure(buffer.queue,sub)//這裏有去重處理
}
}
} else {
for (var i = 0, sub; sub = subs[i++]; ) {
sub.update && sub.update()//最小化刷新DOM樹
}
}
}
buffer對象很簡單
var buffer = {
render: function () {
if (!this.locked) {
this.locked = 1
avalon.nextTick(function () {
buffer.flush()
})
}
},
queue: [],
flush: function () {
for (var i = 0, sub; sub = this.queue[i++]; ) {
sub.update()
}
this.queue.length = this.locked = 0
}
}
這是歷次重構,精簡入口帶來的好處。換言之,作同樣的東西,只有一個函數負責。那麼要對功能作擴展,就直接對 此函數進行處理就好了。
只要減小了視圖無效的刷新,那麼avalon的性能就會當即上去,這對大表格的渲染刷新能立竿見影!
avalon的官網地址: avalonjs.github.io 有關雙緩衝的實現能夠這裏:avalon2.buffer