接上一章 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处理

  1. 过滤监控的属性
  2. 收集视图函数
  3. 转换用于定义

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不能改成vmvar 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: ObjectlastName: "正美"

意图很明显就是遍历这些属性,给出相对应的处理,具体我们接着往下看

纯净的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 {

跳过过滤的条件后:


核心的转换

  • 转换计算属性
  • 转化监控属性

转换计算属性:

  1. 定义时为一个最多拥有get,set方法的对象(get方法是必需的)
  2. 注意,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不能改成vmvar array = (val || "").split(" ");this.firstName = array[0] || "";this.lastName = array[1] || "";},get: function() {return this.firstName + " " + this.lastName;}}

具体有什么用我们接着往下看

转化监控属性

  1. 定义时为一个简单的数据类型,如undefined, string, number, boolean。
  2. 监控数组:定义时为一个数组
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];}});

转载于:https://www.cnblogs.com/aaronjs/p/3146848.html

轻量级前端MVVM框架avalon - 模型转换相关推荐

  1. 轻量级前端MVVM框架avalon - 整体架构

    官网提供架构图 单看这个图呢,还木有说明,感觉有点蛋疼,作者的抽象度太高了,还好在前面已经大概分析过了执行流程 如图 左边是View视图,我们就理解html结构,换句话就是说用户能看到的界面,渲染页面 ...

  2. html 属于mvvm框架,前端MVVM框架avalon揭秘 - HTML编译器

    MVVM试图更加清晰的讲用户界面(UI)开发从应用程序的业务逻辑与行为中心分离,由于,不少这样的模式的实现都须要利用声明式数据绑定来实现讲View(视图)工做从其余层分离css 因此出现了一大堆自定义 ...

  3. 又想到了模板引擎和前端MVVM框架

    最近接手了一个和报表有关的项目.项目后端的大部分工作都是在操作数据库,作为一个后端新手谈不上有什么感觉.但对于看了前端的写法之后,还是有一点点感想. 项目前端主要使用jQuery及其插件,也许这就是大 ...

  4. Fre:又一个小而美的前端MVVM框架

    halo,大家好,好久不贱呢! 最近因为看了一些 be 的小说,整个人都比较致郁::>_<:: 就在昨天,我用了一天的时间写了 fre,又一个小而美的前端MVMM框架 可能你觉得,有了 v ...

  5. Fre:又一个小而美的前端MVVM框架 1

    halo,大家好,好久不贱呢! 最近因为看了一些 be 的小说,整个人都比较致郁::>_<:: 就在昨天,我用了一天的时间写了 fre,又一个小而美的前端MVMM框架 可能你觉得,有了 v ...

  6. 疯狂 java轻量级框架_ViewModel从未如此清爽 - 轻量级WPF MVVM框架Stylet

    Stylet是我最近发现的一个WPF MVVM框架, 在博客园上搜了一下, 相关的文章基本没有, 所以写了这个入门的文章推荐给大家. Stylet是受Caliburn Micro项目的启发, 所以借鉴 ...

  7. tensorflow框架到caffe框架的模型转换

    本文参考以下系列内容: tensorflow2caffe(1) : caffemodel解析,caffemodel里面到底记录了什么? tensorflow2caffe(2) : 如何在tensorf ...

  8. JavaScript富应用MVC MVVM框架

    对框架的挑选 Ember.js.Backbone.js.Knockout.js.Spine.js.Batman.js , Angular.js 1. 轻量级的应用选择哪一个会比较好? 2. 那一个比较 ...

  9. “约见”面试官系列之常见面试题之第九十四篇之MVVM框架(建议收藏)

    目录 一句话总结:vm层(视图模型层)通过接口从后台m层(model层)请求数据,vm层继而和v(view层)实现数据的双向绑定. 1.我大前端应该不应该做复杂的数据处理的工作? 2.mvc和mvvm ...

最新文章

  1. Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
  2. 数百万台车联网设备同时在线0故障,中瑞集团的云原生探索之路 | 云原生Talk
  3. java resource和,java_spring之Autowired与Resource差异解析
  4. linux中查看mysql版本的方法汇总
  5. html中css图片链接地址中有()括号不显示的解决办法
  6. openslide read region 出现黑块 解决办法
  7. 使用jquery完成抽奖图片滚动的效果
  8. 爆肝三天整理!2021年阿里巴巴社招面试题总结,三轮技术面+HR面,总结的明明白白!
  9. G1垃圾回收器详细解读
  10. 数据库组成和存储引擎
  11. sws_getContext函数详细使用
  12. branca.colormap自带的json文件对颜色的定义
  13. UPS不间断电源不工作如何确定是否损坏
  14. c语言从入门到脱发,知乎|脱发是一种怎样的体验?
  15. 苹果ipad邮箱找不到服务器,iPad收发邮件配置教程
  16. 对不起,我就是传说中的 10 倍工程师”
  17. 表空间离线方法大比较
  18. 吴恩达获英特尔投资,创业狂人的三家创业公司今何在?
  19. 寻找拉马努金数(Python实现)
  20. 京东双十一销售额突破2000亿

热门文章

  1. vs code golang代码自动补全
  2. PyQt5简介及demo
  3. bnu 34986 Football on Table(数学+暴力)
  4. Lambda表达式的语法格式
  5. vim 忽略大小写查找
  6. atlsoap.h”: No such file or directory
  7. SQLHELPER C#
  8. Hadoop参数汇总
  9. 从JVM的常见异常来看Tomcat中内存的设置
  10. Maven 之常用命令