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的功能, ...
随机推荐
- 12-4mysql 查询
简单查询select * from 表名; 注意:*代表所有); 查询指定列 select 列名,列名 from 表名 修改结果集的列名select 列名 as'',列名 as'' from 表名 条 ...
- 不让padding影响元素的宽度
CSS3 新增了 box-sizing 属性. 以前,如果指定 div 的宽度为 div { width: 100px; height: 100px; padding: 10px; } 则包含 pad ...
- (实用篇)PHP递归实现无限级分类
在一些复杂的系统中,要求对信息栏目进行无限级的分类,以增强系统的灵活性.那么PHP是如何实现无限级分类的呢?我们在本文中使用递归算法并结合mysql数据表实现无限级分类. 在一些复杂的系统中,要求对信 ...
- python windows终端窗口下输出编码错误
windows简体中文版下终端默认字符集gbk,执行chcp 65001临时修改字符集. 修改默认字符集:注册表HKEY_CURRENT_USER\Console项中CodePage值修改为65001
- std::ostringstream输出流详解
一.简单介绍 ostringstream是C++的一个字符集操作模板类,定义在sstream.h头文件中.ostringstream类通常用于执行C风格的串流的输出操作,格式化字符串,避免申请大量的缓 ...
- hmtl 中的定位
1.绝对定位: position:sbsolute: 作用:将元素从文档流中拖出来,然后使用 left,right,top,bottom属性相对于其最接近的一个具有定位属性的父包含块进行绝对定位. 若 ...
- spring-实现配置文件读取
spring 实现配置读取 Java 的配置读取方式一般是采用java.utils.Properties 或是apache的Configuration工具:然而 spring 框架内置了配置文件的读取 ...
- 解决Maven项目编译时提示:源值1.5已过时,将在未来所有版本中删除
每次编译项目时,都提示:源值1.5已过时,将在未来所有版本中删除 查了一些资料,发现是因为IDEA默认把项目的源代码版本设置为jdk1.5,目标代码设置为jdk1.5 解决方案: 修改Maven的S ...
- fiddler web开发调试工具的使用
fiddler使用场景: (1)开发环境host配置: 通常情况下,配置host需要改变系统文件很不方便,在多个开发环境下切换很低效 fiddler提供了相对高效的host配置方法 (2)前后端接口调 ...
- 【uTenux实验】互斥体
互斥体,维基百科中交互斥锁.其定义是这样的:互斥锁(英语:英语:Mutual exclusion,缩写 Mutex)是一种用于多线程编程中,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制 ...