avalon與雙緩沖技術


avalon1.5一個重要技術升級是引進異步渲染。異步渲染在游戲界有一個更專業的名字,叫雙緩沖。游戲界要刷新界面與我們刷新瀏覽器視圖,面臨的問題是一致的。視圖是由許多存在套嵌關系的方塊組成,它們每一個的改動,都可能引起reflow(其父節點,其父父節點的大小重新計算),這是造成性能問題的關鍵。

雙緩沖技術的主要原理是:當一個動畫爭先顯示時,程序又在改變它,前面的畫面還沒顯示完,程序又要求重新繪制,這樣屏幕就會不停閃爍。為了避免閃爍,可以使用雙緩沖技術,將要處理的圖片都放在內存中處理好過后,再將其顯示到屏幕上。這樣出來的就是完整的圖像,不會出現閃爍現象。

MVVM框架帶來一個革命性的優化是,用戶只操作VM就行了,視圖由框架來同步更新。因此這個同步過程,我們就可以加入優化。比如說angular,就是靠用戶手動調用$apply來驅動臟檢測,只有數據不一致的地方,才會操作DOM。於是沒有了aaa.innerHTML = "xxx&"; aaa.innerHTML = "xxx"的愚蠢代碼。

可能用戶會說我怎么可能會這樣寫呢,那是你因為用於jquery,看到它是在同一個循環中對某個節點執行多次相同的操作。

再回過頭來看avalon。avalon是基於事件驅動(通過Object.defineProperty劫持了對象的屬性),當用戶修改了某屬性,就會立即同步視圖。這種機制最大的好處是,方便與jQuery或其他操作DOM的庫配合使用

          
    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指令加入這樣一行日志:

          
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
            }
        }
    }
})

` 測試頁面為:


<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>

image

在avalon1.5中,打開 avalon.config.async = true開關后,就只輸出兩次。第一次掃描肯定立即出來,以后就是異步了。

image

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樹
     }
}

` 現在改成這樣了

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM