前端MVVM框架avalon - 模型转换1
轻量级前端MVVM框架avalon - 模型转换(一)
接上一章 ViewModel
modelFactory工厂是如何加工用户定义的VM?
附源码
- 洋洋洒洒100多行内部是魔幻般的实现
- 1: function modelFactory(scope) {
- 2: var skipArray = scope.$skipArray, //要忽略监控的属性名列表
- 3: model = {},
- 4: Descriptions = {}, //内部用于转换的对象
- 5: json = {},
- 6: callSetters = [],
- 7: callGetters = [],
- 8: VBPublics = Object.keys(watchOne); //用于IE6-8
- 9: skipArray = Array.isArray(skipArray) ? skipArray.concat(VBPublics) : VBPublics;
- 10: forEach(scope, function(name, value) {
- 11: if (!watchOne[name]) {
- 12: json[name] = value;
- 13: }
- 14: var valueType = avalon.type(value);
- 15: if (valueType === "Function") {
- 16: VBPublics.push(name); //函数无需要转换
- 17: } else {
- 18: if (skipArray.indexOf(name) !== -1) {
- 19: return VBPublics.push(name);
- 20: }
- 21: if (name.charAt(0) === "$" && !systemOne[name]) {
- 22: return VBPublics.push(name);
- 23: }
- 24: var accessor, oldArgs;
- 25: if (valueType === "Object" && typeof value.get === "function" && Object.keys(value).length <= 2) {
- 26: var setter = value.set,
- 27: getter = value.get;
- 28: accessor = function(neo) { //创建计算属性
- 29: if (arguments.length) {
- 30: if (stopRepeatAssign) {
- 31: return; //阻止重复赋值
- 32: }
- 33: if (typeof setter === "function") {
- 34: setter.call(model, neo);
- 35: }
- 36: if (oldArgs !== neo) { //由于VBS对象不能用Object.prototype.toString来判定类型,我们就不做严密的检测
- 37: oldArgs = neo;
- 38: notifySubscribers(accessor); //通知顶层改变
- 39: model.$events && model.$fire(name, neo, value);
- 40: }
- 41: } else {
- 42: if (openComputedCollect || !accessor.locked) {
- 43: collectSubscribers(accessor);
- 44: }
- 45: return value = json[name] = getter.call(model); //保存新值到json[name]
- 46: }
- 47: };
- 48: accessor.nick = name;
- 49: callGetters.push(accessor);
- 50: } else {
- 51: value = NaN;
- 52: callSetters.push(name);
- 53: accessor = function(neo) { //创建监控属性或数组
- 54: if (arguments.length) {
- 55: if (stopRepeatAssign) {
- 56: return; //阻止重复赋值
- 57: }
- 58: if (value !== neo) {
- 59: var old = value;
- 60: if (valueType === "Array" || valueType === "Object") {
- 61: if (value && value.$id) {
- 62: updateViewModel(value, neo, Array.isArray(neo));
- 63: } else if (Array.isArray(neo)) {
- 64: value = Collection(neo, model, name);
- 65: } else {
- 66: value = modelFactory(neo);
- 67: }
- 68: } else {
- 69: value = neo;
- 70: }
- 71: json[name] = value && value.$id ? value.$json : value;
- 72: notifySubscribers(accessor); //通知顶层改变
- 73: model.$events && model.$fire(name, value, old);
- 74: }
- 75: } else {
- 76: collectSubscribers(accessor); //收集视图函数
- 77: return value;
- 78: }
- 79: };
- 80: }
- 81: accessor[subscribers] = [];
- 82: Descriptions[name] = {
- 83: set: accessor,
- 84: get: accessor,
- 85: enumerable: true
- 86: };
- 87: }
- 88: });
- 89: if (defineProperties) {
- 90: defineProperties(model, Descriptions);
- 91: } else {
- 92: model = VBDefineProperties(Descriptions, VBPublics);
- 93: }
- 94: VBPublics.forEach(function(name) {
- 95: if (!watchOne[name]) {
- 96: model[name] = scope[name];
- 97: }
- 98: });
- 99: callSetters.forEach(function(prop) {
- 100: model[prop] = scope[prop]; //为空对象赋值
- 101: });
- 102: callGetters.forEach(function(fn) {
- 103: Publish[expose] = fn;
- 104: callSetters = model[fn.nick];
- 105: fn.locked = 1;
- 106: delete Publish[expose];
- 107: });
- 108: model.$json = json;
- 109: model.$events = {}; //VB对象的方法里的this并不指向自身,需要使用bind处理一下
- 110: model.$watch = Observable.$watch.bind(model);
- 111: model.$unwatch = Observable.$unwatch.bind(model);
- 112: model.$fire = Observable.$fire.bind(model);
- 113: model.$id = generateID();
- 114: model.hasOwnProperty = function(name) {
- 115: return name in model.$json;
- 116: };
- 117: return model;
- 118: }
- VM是用ecma262v5的新API, Object.defineProperties生成的一个充满访问器的对象,这样的对象,能通过用户对它的属性的读写,触发定义时的getter, setter函数。getter, setter对rubyer, pythoner, C#er应该很熟悉,我就不展开了。
- 旧式IE,avalon利用VBScript的类实例,它也存在其他语言的访问器。不过,VBS对象不像JS对象那样随意添加新属性,删除已有属性,因此我们就无法监后添加的新属性。Object.defineProperties也一样,它能处理的属性也只是它定义时的属性,想监控后来的,需要再调用一次Object.defineProperties。
整个工厂方法内部都是围绕着scope处理
- 过滤监控的属性
- 收集视图函数
- 转换用于定义
skipArray //要忽略监控的属性名列表
- 0: "$json"
- 1: "$skipArray"
- 2: "$watch"
- 3: "$unwatch"
- 4: "$fire"
- 5: "$events"
我们还是已官网的demo为列
- 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() {
- return this.firstName + " " + this.lastName;
- }
- }
- })
- avalon.scan(document.querySelector("fieldset"));
此时传入的vm为
- $watch: function noop() {
- firstName: "司徒"
- fullName: Object
- lastName: "正美"
意图很明显就是遍历这些属性,给出相对应的处理,具体我们接着往下看
纯净的js对象,所有访问器与viewModel特有的方法属性都去掉
- 1: if (!watchOne[name]) {
- 2: json[name] = value;
- 3: }
几个简单的条件过滤:
- 1: //判断类型
- 2: var valueType = avalon.type(value);
- 3:
- 4: if (valueType === "Function") {
- 5: // 第一个就是$watch" 被重复假如到列表了
- 6: VBPublics.push(name); //函数无需要转换
- 7: } else {
跳过过滤的条件后:
核心的转换
- 转换计算属性
- 转化监控属性
转换计算属性:
- 定义时为一个最多拥有get,set方法的对象(get方法是必需的)
- 注意,get, set里面的this不能改为vm,框架内部会帮你调整好指向。
判断的条件,值类型是对象,并且有get方法,并且方法要少于等于2个
- if (valueType === "Object" && typeof value.get === "function" && Object.keys(value).length <= 2) {
满足条件的
- 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() {
- return this.firstName + " " + this.lastName;
- }
- }
具体有什么用我们接着往下看
转化监控属性
- 定义时为一个简单的数据类型,如undefined, string, number, boolean。
- 监控数组:定义时为一个数组
- firstName: "司徒"
- accessor[subscribers] = [];
- 别看这个代码是空的函数,不起眼,双向绑定就是看他了,我们先Mark下
- //生成defineProperties需要的配置属性
- Descriptions[name] = {
- set: accessor,
- get: accessor,
- enumerable: true
- };
- Descriptions临时对象 //收集内部用于转换的对象
- enumerable 很重要,为false的话 ,for in就找不到它了
这样循环后就把该干嘛的不该干嘛的都给区分开了
最后都保存在Descriptions中
此时的Descriptions
- 1: Descriptions: Object
- 2:
- 3: firstName: Object
- 4: enumerable: true
- 5: get: function (neo) { //创建监控属性或数组
- 6: set: function (neo) { //创建监控属性或数组
- 7:
- 8: fullName: Object
- 9: enumerable: true
- 10: get: function (neo) { //创建计算属性
- 11: set: function (neo) { //创建计算属性
- 12:
- 13: lastName: Object
- 14: enumerable: true
- 15: get: function (neo) { //创建监控属性或数组
- 16: set: function (neo) { //创建监控属性或数组
看吧就是这样给包装了一下,只是定义了但是还没生效
所以defineProperties(model, Descriptions); 给执行以下 (defineProperties的方法见前面)
model 就是工厂模式转换后的新的vm模型对象了, 因为在开始遍历scope的过滤了一些东东,原本也是用户定义的,所以这时候我们还得加到新的vm-》model中去、
- //添加用户定义的未转换的函数到模型
- VBPublics.forEach(function(name) {
- if (!watchOne[name]) {
- model[name] = scope[name];
- }
- });
前端MVVM框架avalon - 模型转换1的更多相关文章
- 轻量级前端MVVM框架avalon - 模型转换
接上一章 ViewModel modelFactory工厂是如何加工用户定义的VM? 附源码 洋洋洒洒100多行内部是魔幻般的实现 1: function modelFactory(scope) { ...
- 轻量级前端MVVM框架avalon - 初步接触
迷你简单易用的MVVM框架 avalon的介绍http://rubylouvre.github.io/mvvm/ 按照作者的介绍,在HTML中添加绑定,在JS中用avalon.define定义View ...
- 轻量级前端MVVM框架avalon - 执行流程2
接上一章 执行流程1 在这一大堆扫描绑定方法中应该会哪些实现? 首先我们看avalon能帮你做什么? 数据填充,比如表单的一些初始值,切换卡的各个面板的内容({{xxx}},{{xxx|html}}, ...
- 轻量级前端MVVM框架avalon源码分析-总结
距avalon0.7版本发布有一段时间,由于之前的稳定性,就停止一段时间更新,期间研究了下Knockout源码,也尝试写了一个小型的mvvm的实现模型,仅仅只是仿造ko的核心实现,把无关的东西给剥离掉 ...
- 轻量级前端MVVM框架avalon - 执行流程1
基本上确定了avalon的几个重要元素的关系: M,即model,一个普通的JS对象,可能是后台传过来的,也可能是直接从VM中拿到,即VM.$json.有关的这个$json的名字还在商讨 V,即Vie ...
- 轻量级前端MVVM框架avalon - ViewModel
废话说了大几篇,我们开始来点干货了~ ViewModel的内部机制 在MVVM中,数据是核心.而jQuery则以DOM为核心. 而DOM只是HTML在JS的世界的抽象,是一个很易变的东西.因此如果业务 ...
- 轻量级前端MVVM框架avalon - 整体架构
官网提供架构图 单看这个图呢,还木有说明,感觉有点蛋疼,作者的抽象度太高了,还好在前面已经大概分析过了执行流程 如图 左边是View视图,我们就理解html结构,换句话就是说用户能看到的界面,渲染页面 ...
- 轻量级前端MVVM框架avalon - 控制器
引子: 最近工作挺忙,avalon只能断断续续的写下去了,大概看了下angular的源码,看到小一半就比较难坚持了,是块硬骨头,慢慢啃吧 不过angular的的文档中用词还是很优雅: HTML编译器 ...
- 前端MVVM框架avalon揭秘 - HTML编译器
MVVM试图更加清晰的讲用户界面(UI)开发从应用程序的业务逻辑与行为中心分离,因为,很多这样的模式的实现都需要利用声明式数据绑定来实现讲View(视图)工作从其他层分离 所以出现了一大堆自定义的声明 ...
随机推荐
- openstack shelve/unshelve/stop浅析
声明: 本博客欢迎转发,但请保留原作者信息! 博客地址:http://blog.csdn.net/halcyonbaby 内容系本人学习.研究和总结,如有雷同,实属荣幸! stop的虚拟机仅仅是将虚拟 ...
- Java之IO流基础流对象
输入流和输出流是相对于内存设备而言 即将外设中的数据读取到内存中就是输入 将内存中的数据写入到外设中就是输出 字符流的由来: 其实就是:字节流读取文字字节数据后,不直接操作而是先查指 ...
- MVC+Bootstrap设计
MVC+Bootstrap) 二 框架设计 文章目录: 一.前言 二.结构图 三.项目搭建 四.代码生成 五.实现接口 六.依赖倒置 七.登录实现 八.最后 一.前言 这个框架是从最近几年做过的项目中 ...
- sql点滴38—SQL Server 2008和SQL Server 2008 R2导出数据的选项略有不同
原文:sql点滴38—SQL Server 2008和SQL Server 2008 R2导出数据的选项略有不同 说明: 以前要将一个表中的数据导出为脚本,只有用存储过程.现在在SQL Server ...
- AutoCAD 2012安装错误,与.net framework (1603错误)以及ms2005vc++的问题。
首先,这是AutoCAD2012的问题.因为,如果一台计算机已经安装了这些软件,AutoCAD是无法识别出来,因此AutoCAD就只能报错.正确的做法是:如果检测到这些软件已经被安装,则需要忽略这些问 ...
- 在标记的HREF属性中javascript:alert(this.innerHTML)会怎么样?
原文:在标记的HREF属性中javascript:alert(this.innerHTML)会怎么样? <a href="javascript:alert(this.innerHTML ...
- 淘宝code
淘宝code 相信大家都听说过GitHub,也有很多人在用,但是GitHub毕竟在国外,速度不是很给力,而且安装过程也是很漫长.今天来给大家介绍一个国内的免费的开源项目平台,当然也是一个SVN版本控制 ...
- Scala 的 Web 框架 Lift 开始 3.0 版本开发
Scala 的 Web 框架 Lift 开始 3.0 版本开发 http://demo.liftweb.net/ http://liftweb.net/download Lift 框架在不断的成长和改 ...
- 日志分析工具-ApexSQL介绍
原文:日志分析工具-ApexSQL介绍 使用场景:业务数据异常变化,通过代码分析不出来的时候,迫不得已需要通过日志来分析 下载地址:http://www.apexsql.com/Download.as ...
- 【转】Android 工程在4.0基础上混淆
Android现在对安全方面要求比较高了,我今天要做的对apk进行混淆,用所有的第三方工具都不能反编译,作者的知识产权得到保障了,是不是碉堡了. 一,首先说明我这是在4.0基础上进行的. 先看 ...