轻量级前端MVVM框架avalon - 模型转换

简介:

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不能改成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 {

 

跳过过滤的条件后:


核心的转换

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

 

转换计算属性:

  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不能改成vm
                var 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中去、

//添加用户定义的未转换的函数到模型functionif

});


本文转自艾伦 Aaron博客园博客,原文链接:http://www.cnblogs.com/aaronjs/p/3146848.html,如需转载请自行联系原作者

相关文章
|
3天前
|
前端开发 JavaScript
Bootstrap Web 前端 UI 框架
Bootstrap 是快速开发 Web 应用程序的前端工具包。
13 3
|
9天前
|
JavaScript 前端开发 测试技术
前端全栈之路Deno篇(五):如何快速创建 WebSocket 服务端应用 + 客户端应用 - 可能是2025最佳的Websocket全栈实时应用框架
本文介绍了如何使用Deno 2.0快速构建WebSocket全栈应用,包括服务端和客户端的创建。通过一个简单的代码示例,展示了Deno在WebSocket实现中的便捷与强大,无需额外依赖,即可轻松搭建具备基本功能的WebSocket应用。Deno 2.0被认为是最佳的WebSocket全栈应用JS运行时,适合全栈开发者学习和使用。
|
9天前
|
缓存 前端开发 JavaScript
前端serverless探索之组件单独部署时,利用rxjs实现业务状态与vue-react-angular等框架的响应式状态映射
本文深入探讨了如何将RxJS与Vue、React、Angular三大前端框架进行集成,通过抽象出辅助方法`useRx`和`pushPipe`,实现跨框架的状态管理。具体介绍了各框架的响应式机制,展示了如何将RxJS的Observable对象转化为框架的响应式数据,并通过示例代码演示了使用方法。此外,还讨论了全局状态源与WebComponent的部署优化,以及一些实践中的改进点。这些方法不仅简化了异步编程,还提升了代码的可读性和可维护性。
|
10天前
|
前端开发 JavaScript 中间件
前端全栈之路Deno篇(四):Deno2.0如何快速创建http一个 restfulapi/静态文件托管应用及oak框架介绍
Deno 是由 Node.js 创始人 Ryan Dahl 开发的新一代 JavaScript 和 TypeScript 运行时,旨在解决 Node.js 的设计缺陷,具备更强的安全性和内置的 TypeScript 支持。本文介绍了如何使用 Deno 内置的 `Deno.serve` 快速创建 HTTP 服务,并详细讲解了 Oak 框架的安装和使用方法,包括中间件、路由和静态文件服务等功能。Deno 和 Oak 的结合使得创建 RESTful API 变得高效且简便,非常适合快速开发和部署现代 Web 应用程序。
|
17天前
|
存储 人工智能 前端开发
前端大模型应用笔记(三):Vue3+Antdv+transformers+本地模型实现浏览器端侧增强搜索
本文介绍了一个纯前端实现的增强列表搜索应用,通过使用Transformer模型,实现了更智能的搜索功能,如使用“番茄”可以搜索到“西红柿”。项目基于Vue3和Ant Design Vue,使用了Xenova的bge-base-zh-v1.5模型。文章详细介绍了从环境搭建、数据准备到具体实现的全过程,并展示了实际效果和待改进点。
|
17天前
|
JavaScript 前端开发 程序员
前端学习笔记——node.js
前端学习笔记——node.js
33 0
|
17天前
|
人工智能 自然语言处理 运维
前端大模型应用笔记(一):两个指令反过来说大模型就理解不了啦?或许该让第三者插足啦 -通过引入中间LLM预处理用户输入以提高多任务处理能力
本文探讨了在多任务处理场景下,自然语言指令解析的困境及解决方案。通过增加一个LLM解析层,将复杂的指令拆解为多个明确的步骤,明确操作类型与对象识别,处理任务依赖关系,并将自然语言转化为具体的工具命令,从而提高指令解析的准确性和执行效率。
|
17天前
|
存储 弹性计算 算法
前端大模型应用笔记(四):如何在资源受限例如1核和1G内存的端侧或ECS上运行一个合适的向量存储库及如何优化
本文探讨了在资源受限的嵌入式设备(如1核处理器和1GB内存)上实现高效向量存储和检索的方法,旨在支持端侧大模型应用。文章分析了Annoy、HNSWLib、NMSLib、FLANN、VP-Trees和Lshbox等向量存储库的特点与适用场景,推荐Annoy作为多数情况下的首选方案,并提出了数据预处理、索引优化、查询优化等策略以提升性能。通过这些方法,即使在资源受限的环境中也能实现高效的向量检索。
|
17天前
|
机器学习/深度学习 弹性计算 自然语言处理
前端大模型应用笔记(二):最新llama3.2小参数版本1B的古董机测试 - 支持128K上下文,表现优异,和移动端更配
llama3.1支持128K上下文,6万字+输入,适用于多种场景。模型能力超出预期,但处理中文时需加中英翻译。测试显示,其英文支持较好,中文则需改进。llama3.2 1B参数量小,适合移动端和资源受限环境,可在阿里云2vCPU和4G ECS上运行。
|
17天前
|
前端开发 算法 测试技术
前端大模型应用笔记(五):大模型基础能力大比拼-计数篇-通义千文 vs 文心一言 vs 智谱 vs 讯飞vsGPT
本文对比测试了通义千文、文心一言、智谱和讯飞等多个国产大模型在处理基础计数问题上的表现,特别是通过链式推理(COT)提示的效果。结果显示,GPTo1-mini、文心一言3.5和讯飞4.0Ultra在首轮测试中表现优秀,而其他模型在COT提示后也能显著提升正确率,唯有讯飞4.0-Lite表现不佳。测试强调了COT在提升模型逻辑推理能力中的重要性,并指出免费版本中智谱GLM较为可靠。
前端大模型应用笔记(五):大模型基础能力大比拼-计数篇-通义千文 vs 文心一言 vs 智谱 vs 讯飞vsGPT