zoukankan      html  css  js  c++  java
  • 轻量级前端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处理

    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中去、

        //添加用户定义的未转换的函数到模型
        VBPublics.forEach(function(name) {
            if (!watchOne[name]) {
                model[name] = scope[name];
            }
        });
  • 相关阅读:
    尽可能装满的背包问题
    mysql的下载与安装
    IDEA中安装ibatis插件
    tomcat启动失败,提示信息:Unable to ping server at localhost:1099
    http响应头
    查看电脑的IP地址及配置
    IDEA中不同项目配置不同JDK
    Navicat for mysql的下载及破解
    java中String的equals()和 ==
    BeanUtils出现Java.lang.NoClassDefFoundError解决
  • 原文地址:https://www.cnblogs.com/aaronjs/p/3146848.html
Copyright © 2011-2022 走看看