本版本对循环绑定作了巨大改进,感谢@soom, @limodou, @ztz, @Gaubee 提供的大量测试文件。css
咱们看最后一条,咱们能够相似纯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进行讨论,此群为技术群,禁水!