轻量级前端MVVM框架avalon - ViewModel
废话说了大几篇,我们开始来点干货了~
ViewModel的内部机制
- 在MVVM中,数据是核心。而jQuery则以DOM为核心。
- 而DOM只是HTML在JS的世界的抽象,是一个很易变的东西。因此如果业务代码遍历选择器表达式会非常难维护。但不可否认,jQuery是操作DOM的王者,让我们操作DOM顺手拈来。但如果不让你操作DOM,不是更好吗?就像jQuery不让你用getElementById,getElementsByTagName, querySelecterAll,大家都不知道里面有多少坑,短短几个字母$(expr)是背后sizzle选择器引擎1700行的实现!!!!jQuery其实是在用户代码与原生API中提供一层厚厚的粘合层,因此摸起来光溜溜。在MVVM中,DOM操作基本是水下运作了。由于VM与V之间的双向绑定,操作了VM中的数据(当然只能是监控属性),就会同步到DOM,我们透过DOM事件监控用户对DOM的改动,也会同步到VM。DOM隐形了,就像软件公司,到处跑出来活动的是业务员与不写代码的经理老总,程序员全部关起来加班!虽然这比喻有点残酷,但这正体现了各司其职的威力。能说会道去拉风投接单子没什么不妥,喜欢呆在电脑前的就让他呆吧。jQuery的世界就是一个混乱的公司,全能的程序员什么都做。
定义一个ViemMode
<fieldset ms-controller="simple">
<legend>例子</legend>
<p>First name: <input ms-model="firstName" /></p>
<p>Last name: <input ms-model="lastName" /></p>
<p>Hello, <input ms-model="fullName"></p>
<div>{{firstName +" | "+ lastName }}</div>
<p>nick name: <input ms-model="nick.name" /></p>
<p>{{nick.name}}</p>
</fieldset> avalon.define("simple", function(vm) {
vm.firstName = "司徒"
vm.lastName = "正美"
vm.fullName = {//一个包含set或get的对象会被当成PropertyDescriptor,
set: function(val) {//set, get里面的this不能改成vm
var array = (val || "").split(" ");
this.firstName = array[0] || "";
this.lastName = array[1] || "";
},
get: function() {
returnthis.firstName + " " + this.lastName;
}
},
vm.nick = {
name: "暗黑之民"
}
});
这是官方给出的DEMO,我们看看对应的操作定义
HTML中:
- ms-controller是用于指定ViewModel的作用范围, ms-controller的值等于avalon.define的第一个参数,并且这个值必须是一个命法的变量名, 如aaa, $aaa, aaaSSS, aaa_bbb,不能写成23432, sdfs-A
- ms-model="firstName" 此类绑定只能用于表单中,框架会在上面绑定一些事件,如input, change, click以进行同步
- {{firstName +" | "+ lastName }} 模版机制,插值表达式,用于替换值
Javascript中:
- ViewModel的定义,它是通过avalon.define来创建,在函数内我们定义它的属性与方法
- vm.firstName 监控属性:定义时为一个简单的数据类型,如undefined, string, number, boolean。
- vm.fullName 计算属性:定义时为一个最多拥有get,set方法的对象(get方法是必需的),注意,get, set里面的this不能改为vm,框架内部会帮你调整好指向
- 监控数组:定义时为一个数组
- 普通属性或方法:我们可以在vm里面设置一个$skipArray数组,里面装着你不想处理的方法与属性名
因为在ViewModel的转化中会用到defineProperty的定义,有必要先预先提出来
要了解详细,见我的一篇译文 (译)ECMAScript 5 Objects and Properties
JavaScript中有三种不同类型的属性:
命名数据属性(named data properties
命名数据属性,就是我们在IE8碰到的绝对大多数属性,可以随意删除添加,设置什么返回什么,不会在内部做多余的事。
var obj = {
prop: 123
};
console.log(obj.prop); // console.log(obj["prop"]); // obj.prop = "abc";
obj["prop"] = "abc";
命名访问器属性(named accessor properties)
- 命名访问器属性,就是设置或读取时内部调用一些函数做事情的函数,著名的代表是元素的innerHTML,给它一个字符串会创建一大堆节点,读它时返回的值与我们给它的值可能不一样。 又如数组的length,可能通过它来添加或删除元素。IE8添加了set get关键字,不过没什么人用。不过它又添加了著名的Object.defineProperty方法, 里面可指定读取时或写入时的处理函数。标准浏览器老早就支持__defineGetter__,__defineSetter__。
var obj = {}
var _a = 1;
Object.defineProperty(obj, "a", {
get: function() {
return _a
},
set: function(a) {
_a = a + 10
}
}); console.log(obj.a) //1; obj.a = 20; console.log(obj.a) //30;
- 计算属性的set, get函数其实就是对应它们俩。
- avalon, emberjs的ViewModel就是基于访问器实现的,不过emberjs只兼容到IE8。
内部属性就是无法通过JavaScript直接访问的属性
走进vm的幕后:
源码:
1 avalon.define = function(name, deps, factory) {
2
var args = [].slice.call(arguments);
3
if (typeof name !== "string") {
4 name = generateID();
5
args.unshift(name);
6
}
7
if (!Array.isArray(args[1])) {
8 args.splice(1, 0, []);
9
}
10 deps = args[1];
11
if (typeof args[2] !== "function") {
12 avalon.error("factory必须是函数");
13
}
14 factory = args[2];
15
var scope = {
16
$watch: noop
17
};
18
deps.unshift(scope);
19 factory(scope); //得到所有定义 20
var model = modelFactory(scope); //转为一个ViewModel 21 stopRepeatAssign = true;
22 deps[0] = model;
23 factory.apply(0, deps); //重置它的上下文 24
deps.shift();
25 stopRepeatAssign = false;
26 model.$id = name;
27
return avalon.models[name] = model;
28 };
我们一行行分析:
- avalon.define 的定义能接受3个实参
- var args = [].slice.call(arguments); 转换数组,arguments是伪数组
- 保证传参数满足3个定义 如果第二个参数不是数组,转换 avalon.define("on",fn); -> avalon.define("on",[],fn);
var scope = {
$watch: noop
};
定义一个作用域,是一个对象,这个东东其实就是暴露给用户的一个接口,也就是vm了,其实VM是后台先创建的
factory(scope); //得到所有定义
对象嘛是引用,执行后就会把用户定义的方法给挂到scope上了,这样就达到收集用户在外面的处理方法了
var model = modelFactory(scope); //
这个就是核心的东东了,把scpoe转为一个ViewModel,只有转化之后,才能让我们的东东具有实际的处理能力了
factory.apply(0, deps);
这是个非常巧妙的设计,用户定义的函数内部的作用域其实还是在普通的对象,我们可以强制转化vm
return avalon.models[name] = model;
很明显转化后的模型对象挂在到了全局中,方便在扫描节点绑定中获取
所以整个VM的创建过程,
核心点就是
modelFactory方法了
下篇继续着中分析~
轻量级前端MVVM框架avalon - ViewModel的更多相关文章
- 轻量级前端MVVM框架avalon - 初步接触
迷你简单易用的MVVM框架 avalon的介绍http://rubylouvre.github.io/mvvm/ 按照作者的介绍,在HTML中添加绑定,在JS中用avalon.define定义View ...
- 轻量级前端MVVM框架avalon - 执行流程1
基本上确定了avalon的几个重要元素的关系: M,即model,一个普通的JS对象,可能是后台传过来的,也可能是直接从VM中拿到,即VM.$json.有关的这个$json的名字还在商讨 V,即Vie ...
- 轻量级前端MVVM框架avalon - 执行流程2
接上一章 执行流程1 在这一大堆扫描绑定方法中应该会哪些实现? 首先我们看avalon能帮你做什么? 数据填充,比如表单的一些初始值,切换卡的各个面板的内容({{xxx}},{{xxx|html}}, ...
- 轻量级前端MVVM框架avalon源码分析-总结
距avalon0.7版本发布有一段时间,由于之前的稳定性,就停止一段时间更新,期间研究了下Knockout源码,也尝试写了一个小型的mvvm的实现模型,仅仅只是仿造ko的核心实现,把无关的东西给剥离掉 ...
- 轻量级前端MVVM框架avalon - 整体架构
官网提供架构图 单看这个图呢,还木有说明,感觉有点蛋疼,作者的抽象度太高了,还好在前面已经大概分析过了执行流程 如图 左边是View视图,我们就理解html结构,换句话就是说用户能看到的界面,渲染页面 ...
- 轻量级前端MVVM框架avalon - 模型转换
接上一章 ViewModel modelFactory工厂是如何加工用户定义的VM? 附源码 洋洋洒洒100多行内部是魔幻般的实现 1: function modelFactory(scope) { ...
- 轻量级前端MVVM框架avalon - 控制器
引子: 最近工作挺忙,avalon只能断断续续的写下去了,大概看了下angular的源码,看到小一半就比较难坚持了,是块硬骨头,慢慢啃吧 不过angular的的文档中用词还是很优雅: HTML编译器 ...
- 前端MVVM框架avalon - 模型转换1
轻量级前端MVVM框架avalon - 模型转换(一) 接上一章 ViewModel modelFactory工厂是如何加工用户定义的VM? 附源码 洋洋洒洒100多行内部是魔幻般的实现 1: fun ...
- 前端MVVM框架avalon揭秘 - HTML编译器
MVVM试图更加清晰的讲用户界面(UI)开发从应用程序的业务逻辑与行为中心分离,因为,很多这样的模式的实现都需要利用声明式数据绑定来实现讲View(视图)工作从其他层分离 所以出现了一大堆自定义的声明 ...
随机推荐
- 2015 ACM/ICPC EC-Final
A. Boxes and Balls 二分找到最大的不超过$n$的$\frac{x(x+1)}{2}$形式的数即可. #include <bits/stdc++.h> using name ...
- HTC辟谣: HTC Vive2不会在CES 2017上公布
HTC官方:第二代Vive不会在CES 2017上发布.曾有消息称HTC Vive的下一代--Vive 2将在CES 2017上展出能成为首个4K头显以及无线头显.但日前HTC官方给于否认,此消息不实 ...
- Android 学习笔记之一 “Unable to establish loopback connection”
今天碰到一个错误:Unable to establish loopback connection,在网上找各种方法都解决不了,后来看一个帖子说是要关闭系统防火墙,尝试了下还是不行.最后是进任务管理器杀 ...
- Java|今天起,别再扯订阅和回调函数
编程史上有两个令人匪夷所思的说辞,一个是订阅,一个是回调函数. 我想应该还有很多同学为“事件的订阅”和“回调函数”所困扰,因为事情本来就不应该按这个套路来解释. 多直白,所谓的“回调函数”你完全可以线 ...
- mvn使用笔记
mvn命令格式: You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:< ...
- php留言
使用yum安装php yum install pnp -y 安装httpd服务 yum install httpd -y 使用地三方软件将已经制作好的网站如"FileZilla"
- Windows下ADT环境搭建
1.JDK安装 下载JDK(点我下载),安装成功后在我的电脑->属性->高级->环境变量->系统变量中添加以下环境变量: JAVA_HOME值为C:\Program Files ...
- Mysql 学习笔记2
(1)MySQL查看表占用空间大小 //先进去MySQL自带管理库:information_schema //自己的数据库:dbwww58com_kuchecarlib //自己的表:t_carmod ...
- Java环境设置
win7/win8下JDK环境变量设置方法 首先需要到官网上下载JDK这款软件,本人下载的是jdk-7u40-windows-i586版本,安装完成显示jdk1.7.0_67. 其次选择安装路径.本人 ...
- 我的emacs配置
我的emacs配置文件 ;; .emacs ;; ============================== Basic Configure START ====================== ...