avalon 的指令在上一节已经全部介绍完毕,当然有的语焉不详,如ms-js。本节主要总结我对这方面的思考与探索。
MVVM的成功很大一语分是来自于其指令,或叫绑定。让操作视图的功能交由形形式式的指令来代劳。VM,成了一个大管家。它只一个反射体。我们对它的操作,直接影响到视图。因此俗称“操作数据即操作视图”!至于它是怎么影响视图,avalon视其版本的不同,也有不同的解法。如果抛开avalon,纵观世上所有MVVM框架,大抵有如下几种方式
上面说了,既然使用编译整个视图成模板函数这一手段,我们就尽量让这函数轻量化。位于这视图上的所有指设也要简化,方便在对应位置上代入VM中的属性。在avalon1及其他MVVM框架,都是使用动态依赖收集方式来推断指令中的某个单词是否为vm中的某个属性,这性能耗损比较严重。在 avalon2 直接让用户在属性名前加上 **@* * 符号,人工优化这步骤了。此外,指令属性值的设计原则也很明确,就是方便转换一个函数,返回对象或对象数组(如ms-attr,ms-css,ms-widget,ms-effect,ms-class,ms-hover,ms-active,ms-for),少量的返回布尔或字符串等直接可用于JS 语句的字面量。除了ms-for与过滤器,没有其他特殊语法。
除了ms-duplex,ms-on,原则上不再出来ms-xxx-yyy这样的指令。ms-后面只跟一个单词就够了,不用再加-及其他单词。
各种指令的优先级如下:ms-for, ms-widget, ms-effect, ms-if…………其他指令(按指令名的charCodeAt排序)…………ms-duplex!
ms-duplex是最后,因此再不用担心它与其他指令冲突的问题。
指令的更新时机有两个,一个是位于此标签之间的所有孩子执行之前,一个是位于此标签之间的所有孩子执行之后。亦即change, afterChagne列队。有兴趣的话,可以阅览 这里的源码 。
想自定义指令,可以使用avalon.directive方法,第一个为指令名,第二个是定义体,里面至少有parse, diff, update三个方法。自己参看css指令,编写指令吧。
//css指令 var update = require('./_update') avalon.directive('css', { parse: function(cur, pre, binding) { cur[binding.name] = avalon.parseExpr(binding) }, diff: function (copy, src, name) { var a = copy[name] var p = src[name] if (Object(a) === a) { a = a.$model || a//安全的遍历VBscript if (Array.isArray(a)) {//转换成对象 a = avalon.mix.apply({}, a) } if (typeof p !== 'object') {//如果一开始为空 src.changeStyle = src[name] = a } else { var patch = {} var hasChange = false for (var i in a) {//diff差异点 if (a[i] !== p[i]) { hasChange = true patch[i] = a[i] } } if (hasChange) { src[name] = a src.changeStyle = patch } } if (src.changeStyle) { update(src, this.update) } } delete copy[name]//释放内存 }, update: function (dom, vdom) { var change = vdom.changeStyle var wrap = avalon(dom) for (var name in change) { wrap.css(name, change[name]) } delete vdom.changeStyle } })
里面的parse(cur, pre, binding)方法是用于创建虚拟DOM, cur是通过vm.$render方法生成的新虚拟节点,pre是之前的虚拟节点,binding是当前指令抽象生成的绑定对象。
里面的diff(copy, src, name)方法是用来比较前后两个虚拟DOM。copy是新虚拟节点,src是之前的虚拟DOM,name为指令的名字。当你用各种方式比较出这两个虚拟DOM有差异,那你就可以使用require('./_update')这个方法执行更新,更新方式为指令的update方法。
2.1.0后,刷新机制有点改动,两个节点比较出差异后立即更新真实DOM, 不像过去那样全部比较再全量更新。
里面的update(dom,vnode,parent)方法是用来更新真实元素的。
最后你可以在avalon.directives对象中指到所有指令的定义。你也可以在vm.$element.vtree中看到你生成的虚拟DOM树。
既然avalon的指令已经全部介绍完了,因此大家现在可以直接使用 avalon2 了!