迷你MVVM框架 avalonjs 0.85發布


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

  • 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樹。

我們看下面的注解:

<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方法,它里面就默認使用事件進行處理。

        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中的數組

如果有更好的算法,請多多指教。

經過這次大重構后,avalon在API上基本沒有變化了,未來的v0.9就是fix BUG然后發布正式版。

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

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

大家可以加入QQ群:79641290進行討論,此群為技術群,禁水!


免責聲明!

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



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