迷你MVVM框架avalonjs 实现上的难点有哪些

经过两个星期的性能优化,avalon终于实现在一个页面绑定达到上万个的时候不卡顿的目标(angular的限制是2000)。现在稍作休息,总结一下avalon遇到的一些难题。

首先是如何监控的问题。所有MVVM要将VM中的属性与视图中的绑定属性关联起来大抵有如下三种方式:angular是对函数体取toString进行预编译,将里面的赋值语句,取值语句替换为set,get方法,然后通过特定方法进行脏检测触发,或手动触发;ko是对VM的属性用监控函数外包一层,全事件驱动触发;avalon是通过Object.defineProperties重写内部set,get函数,全事件驱动触发。此外还有emberjs,它是统一使用上帝set,get方法接触所有取值赋值的入口,全事件驱动触发,算是angular的改良。从用户体验来说,avalon的实现是最好的,因为它是不用改变用户习惯,emberjs次之,强制使用set,get方法,ko让函数、数组变成了函数,让用户感到非常违和,angular最差,一大堆恶心的限制,无法直接操作VM。出现这局面是因为Object.defineProperty不好兼容,它虽然是IE8支持,但那只在元素节点上存在。除非你摒弃IE8了。直接我找到VBScript,这问题才不算问题。我的优势是,我至一开始就知道有VBS这东西存在,在avalon实现之初就开始动用这东西。

ms-if的实现,说到底是生命周期的设计问题,如何销毁一个绑定及在特殊情况还让它继续存活。VM与视图的关联点在于绑定属性,绑定属性会转换为求值函数,求值函数将与它的上下文环境(比如它所在的元素节点,它原来的绑定属性的名字,值,类型,过滤器定义情况等等)组成一个对象,放到一个数组中。这就是订阅者数组。数VM中的属性发生变化时(通过内部set方法被调动时得知),就会执行这个求值函数将其他东西一起执行,从而实现视图的最小化局部刷新。问题是,我们的页面有时很大,上面拥有许多绑定属性,这意味着这些中间生成的求值函数与对象将一直放在各个订阅者数组中,占用着大量内存。如果再出现像瀑布流或或定时刷新的情况,这内存占用将越来越大,让页面运行缓慢。因此就必需考虑回收内存的情况了。avalon给出的方案时,当某一个节点将出DOM树,它自身或底下的节点的原来所有绑定属性所生成的求值函数将从订阅者数组中移除。

function notifySubscribers(accessor) { //通知依赖于这个访问器的订阅者更新自身
    var list = accessor[subscribers]
    if (list && list.length) {
        var args = aslice.call(arguments, 1)
        for (var i = list.length, fn; fn = list[--i]; ) {
            var el = fn.element,
                    remove
            if (el && !avalon.contains(ifSanctuary, el)) {
                if (typeof el.sourceIndex == "number") { //IE6-IE11
                    remove = el.sourceIndex === 0
                } else {
                    remove = !avalon.contains(root, el)
                }
                if (remove) { //如果它没有在DOM树
                    list.splice(i, 1)
                    log("Debug: remove " + fn.name)
                }
            }
            if (typeof fn === "function") {
                fn.apply(0, args) //强制重新计算自身
            } else if (fn.getter) {
                fn.handler.apply(fn, args) //处理监控数组的方法
            } else {
                fn.handler(fn.evaluator.apply(0, fn.args || []), el, fn)
            }
        }
    }
}

上面就是这一操作的实现,VM要对视图进行同步都必须经过此方法notifySubscribers。每次执行时,它都会取得求值函数上的元素节点,然后判定它是否在DOM树上。在IE6-11中,我们可以判定sourceIndex 属性是否为零得知,标准浏览器可以通过根节点是否包含当前元素得知(这个contains方法内部需要做一番兼容)。

本栏目更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/webkf/tools/

但ms-if的出现打破这和谐局面,要考虑此绑定主动移出DOM树的情况,还要判定考虑此元素什么时候插回DOM树。在循环绑定中,元素节点在一开始是在文档碎片中动态生成的,这个更麻烦。从0.6-1.1,我一直陷入这噩梦中。之前我有的方案是采用定时器,不断轮询此节点是否插入DOM,插入了才开始对它扫描。后来又发掘出DOMNodeInserted这个事件,对一些高级一点的浏览器做一些优化。到了0.982,干脆就直接假设它们一开始都没加入DOM,添加一个类名,防止移入移出时颤动,再把当前的VM列表绑定在元素上,然后判定元素是否在DOM树(又是轮询操作)。再后来是改写循环绑定部分,在ms-each, ms-repeat等执行后,再执行一个回调,扫描当前部分,这样就可以消去轮询操作了。再再后来的改进是,确保循环生成时,元素都集中一个文档碎片中,然后整体插入DOM时,这时才进行扫描。换言之,第一次它总是在DOM树里。于是就能消去contains判定,ms-if的代码大大减少。现在的ms-if今非昔比,还加入了按需加载功能。它的子元素扫描被它的绑定属性所控制,对大页面的性能优化非常有用。

批量生成与监控数组的实现,这俩是相辅相成的。早期的监控是直接在原数组中改,因此原工厂函数非常庞大。后来直接把这些要覆盖的函数放到一个对象上,然后工厂方法里直接mix一下就行了。还简接让所有监控数组共享了这些方法,节省内存。在绑定的实现,之前是有许多分支,什么push, unshift, pop, shift, set, reroder, splice, clear一大堆,那个视图刷新函数太苦逼了。后来对数组的操作进行深入分析,发现所有操作无疑是做以下几种操作,添加元素,删除元素,改写元素对应的索引值,移动元素到某一位置,直接替换元素。于是改写监控数组的方法,根据add. del, index, move这四种操作进行组合(0.9.0),后来还加了clear,因为批量处理一个数组或一个子对象都用到此操作。这些操作里面都会通过notifySubscribers方法,将操作名与相应参数传到视图刷新函数,从而分配到不同分支上做DOM处理。这算是成功了一大步。内部其实还涉及到代理VM的生成算是处理,于是有了createItemModel的内部函数,然后出现了ms-with,于是它们改名了createWithModel, createEachModel。这两个方法的实现也不断改进,后来更名为createEachProxy, creatWithProxy,在ms-with里还使用了对象池技术(withMapper ,0.96),重用所有同名的键值对生成的代理对象。

到0.9.8,偷偷引入一个ms-repeat绑定。avalon早期的参考对象是knockout,它实现循环绑定时需要用到两个元素,一个父元素作容器,它下面的所有节点作模板,或者用一个虚拟节点(真实名字是两个一前一后的注释节点)圈定作用范围,里面的那些子点作模板。由于注释节点在IE6-8的UL,OL元素上会发生错乱,需要手动处理,avalon就没有更进。但在许多场合,总要外套一个父节点是非常难办,或做不到,于是移目于angular上。angular的ng-repeat只循环元素自身是一个非常好的方案,加之它又带来了$first, $last, $remove等好东西,于是avalon开始模仿。但这工程量与难度非常大,一直跌跌撞撞,在1.2时才基本算完工。其间要处理的问题是,如何让ms-repeat如何同时遍历数组与对象,对象的键值对的输出顺序(data-with-sorted回调的引进),批处理后的回调(data-*-rendered回调),回滚机制(rollback函数),如何判定子元素已经被渲染(需要在元素上添加一个标记,放便在scanAttr时执行一个回调)。回调是同事在做私自人项目提出的,最初没参数,现在能明确是add, del, index等操作了。生成代理VM与绑定标记后来抽象成一个shimController,实现批量插入与批量处理。对象池(更名为withProxyPool)也大大优化,它在一开始时就生成所有键值对代理VM,不再在求值函数里判定了。并且VM加了一个withProxyCount,进行优化。
目前批处理涉及到的内部方法与对象

createWithProxy 创建循环对象时的代理VM

createEachProxy创建循环数组时的代理VM

updateWithProxy 更新某一键值对的代理VM

withProxyPool createWithProxy生成的对象统一放在这里管理,防止重复生成

removeView 批量移除一堆节点

getLocatedNode 定位要插入的位置

shimController 为ms-each, ms-with, ms-repeat要循环的元素外包一个msloop临时节点,ms-controller的值为代理VM的$id,同时是实现批量插入插量移除临时节点的关键

removeFromSanctuary 将通过ms-if移出DOM树放进ifSanctuary的元素节点移出来,以便垃圾回收

queryComments 得到某一元素节点或文档碎片对象下的所有注释节点

iteratorCallback 通过它执行data-*-rendered回调

最后一个也是最难一个至少也没有搞定,只在不断改良中,这就是UI绑定的设计。之前有一个绑定叫ms-ui,已经夭折。现在的ms-widget还是不够好。有时我想参考angular的那种方式,但又嫌它添加了太多莫名其妙的符号。但主要是因为我的框架对用户是非常放纵,不喜欢那种改配置的设计。不过就是放着现在的不管,它还有一个重大的缺陷,没有生命周期管理。这个在项目中已经暴露出来,需要用户自己定义一个destroy方法,手动销毁。我认为这是框架的份内事。接下来几星期,我就着手这方面的改进,希望能把这痛点解决掉。

最后总结一下:

VM实现,如何内置与V的同步机制。

ms-if,ng-if, data-bind="if:xxx"这样插入移除的绑定,会中断之前无悬念一直扫到底的思路,之前想好的生命周期管理也要出岔子了!

ms-each, ms-repeat, ng-repeat,data-bind="foreach:xxx"这样的批量生成的绑定,这种绑定最容易引起性能问题,并且需要界定其作用范围

ms-widget, <widget></widget>这样转换元素为一个控件的绑定,这也最复杂最麻烦的绑定,需要有通盘的设计观。

当然对于刚接触这领域的人可以还有许多麻烦事,如不使用jQuery的情况如何摆平那一大堆兼容问题,如何写一个parser解析绑定属性的值,加载器,路由器,动画引擎等一大堆配套设施……这要你自求多福,好之为之了,但跨过这道坎,你就是另一个级别的人物了!

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索数组
, 对象
, 函数
, mvvm
, mvvm框架的avalon
, 节点
, 元素
, 一个
, ng-repeat
, ng-if
, ng-repeat遍历渲染
, angular过滤器
, avalon绑定属性
绑定数组
mvvm框架、wpf mvvm框架、mvvm框架有哪些、android mvvm框架、mvvm模式的框架有哪些,以便于您获取更多的相关知识。

时间: 2024-09-20 17:49:46

迷你MVVM框架avalonjs 实现上的难点有哪些的相关文章

迷你 MVVM 框架 avalonjs 1.3.6 发布

本版本是一次重要的升级,考虑要介绍许多东西,也有许多东西对大家有用,也发到首页上来了. 本来是没有1.36的,先把基于静态收集依赖的1.4设计出来后,发现改动太多,为了平缓升级起见,才减少了一部分新特性,做成1.36.因此是先有1.4,才有1.36. 本版本针对公司(去哪儿网,毕竟是带薪在公司里搞这框架)的访问浏览器的占有率,加大对国产浏览器的测试.涉及浏览器有QQ浏览器, 搜狗浏览器, 猎豹浏览器, 傲游浏览器,但没有360浏览器,我们公司的同事还是很注意安全的.这么多浏览器,现在还差两个ca

简述迷你MVVM框架avalon在兼容旧式IE所做的努力

很多时候,写代码就像砌砖头,只要我们不关心盖楼的原因.建筑的原理.土木工程基础和工程经验,就算我们砌了100栋高楼,我们也就只是一个砌砖工人,永远也成为不了一个工程师,更别说建筑师了.而那些包工头也只会把我们当成劳动力罢了.--左耳朵耗子 avalon在兼容旧式IE上做了大量工作,从而让它更接地气,完美地运行于国内的各种奇葩浏览器中. 首先是Object.defineProperties的模拟,正因为有这东西,才能让avalon是纯事件驱动地同步视图,而不用脏检测,从而获得更高的性能. //IE

vue,angular,avalon这三种MVVM框架优缺点_其它

本文的主要内容是参考官方文档说明总结而来: Vue.js Vue.js @尤雨溪 老师写的一个用于创建 web 交互界面的库,是一个精简的 MVVM.从技术角度讲,Vue.js 专注于 MVVM 模型的 ViewModel 层.它通过双向数据绑定把 View 层和 Model 层连接了起来.实际的 DOM 封装和输出格式都被抽象为了Directives 和 Filters.Vue.js和其他库相比是一个小而美的库,作者的主要目的是通过一个尽量简单的 API 产生可反映的数据绑定和可组合的视图组件

Silverlight实用窍门系列:52.Silverlight中的MVVM框架极速入门(以MVVM Light Toolkit为例)

 在本文将以MVVM Light Toolkit为例讲解MVVM框架在现实中的使用入门,首先我们在http://mvvmlight.codeplex.com/下载它的MVVM框架下来.也可以通过 http://files.cnblogs.com/chengxingliang/GalaSoft.MvvmLight.V3.rar 下载MVVM Light Toolkit.然后我们安装这个安装包,然后重新打开VS2010,新建一个项目,如下图所示:    Tip:MVVM分为Model.ViewMod

.NET的MVVM框架

转自 http://www.infoq.com/cn/news/2011/12/mvvm-frameworks-net 作者 Roopesh Shenoy 译者 李永伦 发布于 2011年12月15日 Model-View-ViewModel是 一种架构模式,主要在WPF.Silverlight和WP7开发里使用,它的目标是从视图层移除几乎所有代码隐藏(code-behind).交互设计 师可以专注于使用XAML表达用户体验需求,然后创建和视图模型的绑定,而视图模型则是由应用程序开发者开发和维护

MVC, MVP, MVVM比较以及区别(上)

原文:MVC, MVP, MVVM比较以及区别(上) MVC, MVP和MVVM都是用来解决界面呈现和逻辑代码分离而出现的模式.以前只是对它们有部分的了解,没有深入的研究过,对于一些里面的概念和区别也是一知半解.现在一边查资料,并结合自己的理解,来谈一下对于这三种模式思想的理解,以及它们的区别.欢迎各位高手拍砖. 阅读目录: 一. MVC, MVP, MVVM诞生的需求? 二. 一段典型的耦合代码 三. MVC模式      3.1 主动MVC      3.2 被动MVC      3.3 W

基于WPF系统框架设计(6) 整合MVVM框架(Prism)

我们基础的框架已经搭建起来了,现在整合MVVM框架Prism,在ViewModel做一些逻辑处理,真正把界面设 计分离出来. 这样方便我们系统开发分工合作,同时提高系统可维护性和灵活性. 具体的 Prism安装和Microsoft.Practices.Prism.dll获取,在这个网址:http://compositewpf.codeplex.com/ 跟Winform一样原始的模式: (1)现在看一下之前的设计的View: MainWindow.XAML源码: (2)MainWindow.xa

怎样在不使用框架的基础上开发一个 Javascript 组件

本文讲的是怎样在不使用框架的基础上开发一个 Javascript 组件, 许多开发者(包括我)犯的一个错误是当遇到问题时他们总是自上而下地考虑问题.他们想问题的时候,总是从考虑框架(Framework),插件(Plugin),预处理器(Pre-processors),后处理器(Post-processors),面向对象模式(objected-oriented patterns)等等这些方面出发,他们也可能会从他们以前看过的一篇文章来考虑.而这时如果有一个生成器(Generator)的话,他们当然

frame页面框架设计,上左右结构

问题描述 frame页面框架设计,上左右结构 求一个frame页面框架设计,上左右结构,左边两个框架是上下,右边一个框架 解决方案 <html><body><table border='1' width=""100%"" height=""100%""><tr> <td ><iframe src=''></iframe></td>