迷你MVVM框架 avalonjs 0.85發佈

本版本對循環綁定作了巨大改進,感謝@soom, @limodou, @ztz, @Gaubee 提供的大量測試文件。css

  • fix scanNodes, 在循環綁定(ms-each)掃描元素節點時必須 nextTick,不然舊式IE會忙碌不過來。
  • fix ms-css ,舊式IE style[name] = value, 當value爲NaN ,不帶單位或不是數值什麼會拋異常,須要try catch。
  • 舊式IE下有些元素的innerHTML是隻讀的, 所以不能一概使用innerHTML,而且有些元素的生成,如script標籤是不會執行,爲此我引入新的parseHTML模塊來處理此事。
  • fix AMD 加載由於手誤進錯分支的BUG
  • fix scanExpr bug, 它在IE10有時會多生成一個綁定對象,異致不渲染錯誤。
  • 重構Collection內部對象與ms-each綁定,引入「事務」的概念,讓其插入節點時更加智能高效。

咱們看最後一條,咱們能夠相似純JS操做爲內存操做,DOM操做爲IO操做,執行一萬次前者所需的時間可能還比不上一次後者的。DOM操做的開銷就是這麼大。有的DOM操做還會引發reflow,這危害更大。所以明智的作法就是將要操做的節點移出DOM樹。更好的辦法是,此多個DOM操做合成一個,所有在文檔碎片中搞完才插入DOM樹。html

咱們看下面的註解:git

<div ms-controller="box">
      <div ms-each-el="array" id="aaa">
           <p>{{$index}}----{{el}}</p>
      </div>
</div>
avalon.define("box", function(vm) {
       vm.array = [1, 2, 3, 4, 5]
})


實現過程
當掃描到div#aaa 將div#aaa的全部子節點複製一份到文檔碎片vTemplate

執行begin命令,將vTemplate複製一個空的文檔碎片vTransation( cloneNode(false) ), 設置全局變量flagTransation = true;


    開始循環數組
  
    執行insert命令
    將vTemplate複製一個文檔碎片vEl( cloneNode(true) ), 
  將對應的子VM與它進行掃描
  此時它的內容應爲 <p>0 --- 1</p>
    將vEl appendChild到 vTemplate
    .....
    重複執行array.length次

執行commit命令,將vTemplate append到div#aaa節點中, 設置全局變量flagTransation = false
從新排列全部$index

在數組有關添加元素的push, unshift, splice這三個方法中,都調用了add方法,它裏面就默認使用事件進行處理。github

        array._splice = array.splice
        array.add = function(arr, insertPos) {
            insertPos = typeof insertPos === "number" ? insertPos : this.length;
            notifySubscribers(this, "begin")
            for (var i = 0, n = arr.length; i < n; i++) {
                var el = convert(arr[i])
                var pos = insertPos + i
                this._splice(pos, 0, el)
                notifySubscribers(this, "insert", pos, el)
            }
            notifySubscribers(this, "commit", insertPos)
            if (!this.stopFireLength) {
                return dynamic.length = this.length
            }
        }

notifySubscribers會向上通知updateListView方法,而後讓它執行相關的DOM操做算法


                case "begin":
                    list.vTransation = data.vTemplate.cloneNode(false)
                    flagTransation = true
                case "insert":
                    //將子視圖插入到文檔碎片中
                    var tmodel = createVModel(pos, el, list, data.args)
                    var tview = data.vTemplate.cloneNode(true)
                    tmodel.$view = tview
                    vmodels = [tmodel].concat(vmodels)
                    tmodels.splice(pos, 0, tmodel)
                    scanNodes(tview, vmodels);
                    data.group = ~~tview.childNodes.length //記錄每一個模板一共有多少子節點
                    list.vTransation.appendChild(tview)
                    break
                case "commit":
                    pos = ~~pos
                    //獲得插入位置 IE6-10要求insertBefore的第2個參數爲節點或null,不能爲undefined
                    var insertNode = parent.childNodes[ data.group * pos] || null
                    parent.insertBefore(list.vTransation, insertNode)
                    flagTransation = false
                    resetItemIndex(tmodels)
                    break

嘛,不過此次改動太大了,有關Collection與bindingHandlers["each"]的代碼都幾乎改清光。另外一個值得一提的是VM數組在腓序時,與視圖的同步。這裏涉及如何讓一個數組基於另外一個數組進行排序,個人解決方式以下:數組

  var aaa = [1, 2, 3, 4, 5, 1]
            var bbb = [{v: 2}, {v: 3}, {v: 1}, {v: 1}, {v: 5}, {v: 4}]
            var swapTime = 0
            var isEqual = Object.is || function(x, y) {//主要用於處理NaN 與 NaN 比較
                if (x === y) {
                    return x !== 0 || 1 / x === 1 / y;
                }
                return x !== x && y !== y;
            };
            for (var i = 0, n = bbb.length; i < n; i++) {
                var a = aaa[i];
                var b = bbb[i]
                var b = b && b.v ? b.v : b
                if (!isEqual(a, b)) {
                    console.log(++swapTime)
                    var index = getIndex(a, bbb, i);
                    var el = bbb.splice(index, 1)
                    bbb.splice(i, 0, el[0])
                }
            }
            function getIndex(a, bbb, start) {
                for (var i = start, n = bbb.length; i < n; i++) {
                    var b = bbb[i];
                    var check = b && b.v ? b.v : b
                    if (isEqual(a, check)) {
                        return i
                    }
                }
            }
            console.log(JSON.stringify(bbb))
//在框架中,aaa爲數據模型M中的數組,bbb爲視圖模型VM中的數組

若是有更好的算法,請多多指教。ruby

通過此次大重構後,avalon在API上基本沒有變化了,將來的v0.9就是fix BUG而後發佈正式版。app

迷你MVVM框架在github的倉庫https://github.com/RubyLouvre/avalon框架

官網地址http://rubylouvre.github.io/mvvm/mvvm

你們能夠加入QQ羣:79641290進行討論,此羣爲技術羣,禁水!

相關文章
相關標籤/搜索