avalon2学习教程15指令总结
avalon的指令在上一节已经全部介绍完毕,当然有的语焉不详,如ms-js。本节主要总结我对这方面的思考与探索。
MVVM的成功很大一语分是来自于其指令,或叫绑定。让操作视图的功能交由形形式式的指令来代劳。VM,成了一个大管家。它只一个反射体。我们对它的操作,直接影响到视图。因此俗称“操作数据即操作视图”!至于它是怎么影响视图,avalon视其版本的不同,也有不同的解法。如果抛开avalon,纵观世上所有MVVM框架,大抵有如下几种方式
- 函数wrapper:将原数据对象重新改造,所有属性都变成一个函数,有参数时就是赋值,进行视图同步与回调派发,没有参数时就取值,进行依赖收集。如knockout.js。
- 上帝getter,setter: 将原数据对象重新包装,但对数据的操作必须经过统一的set,get方法。在set方法进行视图同步与回调派发,没有参数时进行依赖收集。如reactive.js。如果放松要求,react也是这种方式,它使用setState进行视图同步。但它们依赖收集的过程。
- 函数编译及脏检测:将VM放到一个函数体内,取toString重新编译,内部是第一种方式。如angular.
- Object.defineProperty属性劫持:前三种的用户体验非常糟,于是有了这种方式,最初我是从emberjs中学来的。avalon,vue及其他后来的MVVM框架都是使用这种方式。在赋值时执行内部的setter方法,进行视图同步与回调派发,在取值时执行内部的getter方法,进行依赖收集。
- Object.observe对象监控: 这个API很短命,因此没几个MVVM框架用上它。由于无法进行依赖收集,需要别辟蹊径!
- Proxy对象监控:Object.observe对用户的行为监控是很弱的,并且是异步的,不够友好。于是有了这个新宠物。但这其实也不算新宠,firefox4就存在了。它能对数据赋值,取值,遍历,删除等各种行为都能监控到,了解Object.defineProperty 不能监控新属性的难题。在avalon2 中,就有一分支使用它实现。avalon2一共使用了VBScript, Object.defineProperty, Proxy实现vm。并且它没有进行依赖数据,而是将整个视图编译成一个大函数,每次数据变动,都重新执行这个函数,产生虚拟DOM,前后虚拟DOM进行diff,最后全量更新。这思路从react.js学来的。
上面说了,既然使用编译整个视图成模板函数这一手段,我们就尽量让这函数轻量化。位于这视图上的所有指设也要简化,方便在对应位置上代入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了!
avalon2学习教程15指令总结的更多相关文章
- avalon2学习教程09循环操作
avalon2的循环指令的用法完全改变了.avalon最早期从knockout那样抄来ms-each,ms-with,分别用于数组循环与对象循环.它们都是针对元素内容进行循环.后来又从angular那 ...
- avalon2学习教程14动画使用
avalon2实际上没有实现完整的动画模块,它只是对现有的CSS3动画或jquery animate再包装一层. 我们先说如何用CSS3为avalon实现动画效果.首先要使用avalon.effect ...
- avalon2学习教程13组件使用
avalon2最引以为豪的东西是,终于有一套强大的类Web Component的组件系统.这个组件系统媲美于React的JSX,并且能更好地控制子组件的传参. avalon自诞生以来,就一直探索如何优 ...
- avalon2学习教程12数据验证
avalon2砍掉了不少功能(如ms-include,ms-data),腾出空间加了其他更有用的功能.数据验证就是其中之一.现在avalon2内置的验证指令是参考之前的oniui验证框架与jquery ...
- avalon2学习教程08插入移除操作
本节介绍的ms-if指令与ms-visible很相似,都是让某元素"看不见",不同的是ms-visible是通过CSS实现,ms-if是通过移除插入节点实现. ms-if的用法与1 ...
- avalon2学习教程06样式操作
avalon2的ms-css的变革思路与ms-attr一样,将多个操作合并成到一个对象里面处理,因此没有ms-css-name="value",只有ms-css="Obj ...
- avalon2学习教程05属性操作
avalon2与avalon1的属性操作虽然都是使用ms-attr,但用法完全不一样. avalon1是这样操作属性的 其语法为 ms-attr-valueName="vmProp" ...
- avalon2学习教程 03数据填充
数据填充是一个模版最基础的功能,直接从JSON(vm)取出数据,放到适当的位置上.在静态模板中,不区分文本与HTML,只看你的字符串是否有< >来决定生成文本节点与元素节点.但MVVM中, ...
- avalon2学习教程01
经过难苦奋战,avalon2终于面世了.这花了大半年时间,其中1.6还胎死腹中.长达半年没有产出,我都担心自己会被裁掉…… avalon2许多API与1.4.×保持一致,当然也添加了一些1.5的功能, ...
随机推荐
- 2015GitWebRTC编译实录10
2015.07.20 rtc_p2p编译通过[879/1600 ] CXX obj /webrtc/p2p/client/rtc_p2p.httpportallocator.o[880/1600 ] ...
- Selenium定位一 --单个元素定位方法
Selenium-Webdriver 提供了强大的元素定位方法,支持以下三种方法. 单个对象的定位方法 多个对象的定位方法 层级定位 定位单个元素在定位单个元素时,selenium-webdriver ...
- 类似title的鼠标跟随事件
$(document).ready(function(){ // 创建一个div显示提示信息 var dropTitle = document.createElement("div" ...
- Spring源码学习之:ClassLoader学习(1)
转载:http://longdick.iteye.com/blog/442213 java应用环境中不同的class分别由不同的ClassLoader负责加载. 一个jvm中默认的classloade ...
- Angular2.0-组件
截止到目前为止,Angular2.0完成了其alpha-32版本的开发,新的版本还在迭代开发当中,这其中有个问题,就是每个版本相比于以前的版本都会有一些改动,包括API方面的修改,这会导致很多基于以前 ...
- Mac OS X使用快捷键改善窗口管理的六个方法
http://www.macx.cn/thread-2085916-1-1.html 窗口全屏 ctrl+command+f
- thrift demo
基于上一篇博客,安装thrift complier之后,就需要进行跑跑程序,来看看是否如同预期的那种效果. 前面的thrift compiler的主要作用,其实就是为了IDL的,就是防止客户端和服务端 ...
- 【转】编译quickfast解析库(沪深level2行情转码库)
转自http://blog.csdn.net/hacode/article/details/7065889 编译quickfast解析库(沪深level2行情转码库) 目录(?)[-] 1 下载源代 ...
- ajax 开始的loading加载
注意是异步加载方式 $.ajax({ beforeSend:function(){ $("#showMes").html('loading...'); }, success: f ...
- shiro 标签
在使用Shiro标签库前,首先需要在JSP引入shiro标签: <%@ taglib prefix="shiro" uri="http://shiro.apache ...