轻量级前端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中:

  1. ms-controller是用于指定ViewModel的作用范围, ms-controller的值等于avalon.define的第一个参数,并且这个值必须是一个命法的变量名, 如aaa, $aaa, aaaSSS, aaa_bbb,不能写成23432, sdfs-A  
  2. ms-model="firstName" 此类绑定只能用于表单中,框架会在上面绑定一些事件,如input, change, click以进行同步
  3. {{firstName +" | "+ lastName }} 模版机制,插值表达式,用于替换值

    

     Javascript中:

  1. ViewModel的定义,它是通过avalon.define来创建,在函数内我们定义它的属性与方法
  2. vm.firstName 监控属性:定义时为一个简单的数据类型,如undefined, string, number, boolean。
  3. vm.fullName 计算属性:定义时为一个最多拥有get,set方法的对象(get方法是必需的),注意,get, set里面的this不能改为vm,框架内部会帮你调整好指向
  4. 监控数组:定义时为一个数组
  5. 普通属性或方法:我们可以在vm里面设置一个$skipArray数组,里面装着你不想处理的方法与属性名

   

因为在ViewModel的转化中会用到defineProperty的定义,有必要先预先提出来 

要了解详细,见我的一篇译文 (译)ECMAScript 5 Objects and Properties

   

JavaScript中有三种不同类型的属性:

命名数据属性(named data properties

     命名数据属性,就是我们在IE8碰到的绝对大多数属性,可以随意删除添加,设置什么返回什么,不会在内部做多余的事。

复制代码
var obj = {
    prop: 123
};
console.log(obj.prop); // 123
                
console.log(obj["prop"]); // 123
            
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方法了

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


相关文章
|
2天前
|
存储 NoSQL 前端开发
前端轻量级数据库mongodb
【10月更文挑战第2天】MongoDB 是一个基于分布式文件存储的开源数据库系统,不属于前端轻量级数据库,而是后端数据库。它使用 BSON 格式存储数据,支持复杂的数据结构,适用于内容管理系统、物联网等领域。MongoDB 通过动态模式和面向对象的数据存储方式,提供了灵活的数据模型。在 Web 应用中,它通常作为后端存储,通过 API 与前端交互,实现高效的数据管理和实时更新。
|
8天前
|
前端开发 JavaScript API
React、Vue.js 和 Angular前端三大框架对比与选择
前端框架是用于构建用户界面的工具和库,它提供组件化结构、数据绑定、路由管理和状态管理等功能,帮助开发者高效地创建和维护 web 应用的前端部分。常见的前端框架如 React、Vue.js 和 Angular,能够提高开发效率并促进团队协作。
24 4
|
15天前
|
机器学习/深度学习 数据采集 JavaScript
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
ADR药品不良反应监测系统是一款智能化工具,用于监测和分析药品不良反应。该系统通过收集和分析病历、处方及实验室数据,快速识别潜在不良反应,提升用药安全性。系统采用Java开发,基于SpringBoot框架,前端使用Vue,具备数据采集、清洗、分析等功能模块,并能生成监测报告辅助医务人员决策。通过集成多种数据源并运用机器学习算法,系统可自动预警药品不良反应,有效减少药害事故,保障公众健康。
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
|
27天前
|
Web App开发 前端开发 JavaScript
Web前端项目的跨平台桌面客户端打包方案之——CEF框架
Chromium Embedded Framework (CEF) 是一个基于 Google Chromium 项目的开源 Web 浏览器控件,旨在为第三方应用提供嵌入式浏览器支持。CEF 隔离了底层 Chromium 和 Blink 的复杂性,提供了稳定的产品级 API。它支持 Windows、Linux 和 Mac 平台,不仅限于 C/C++ 接口,还支持多种语言。CEF 功能强大,性能优异,广泛应用于桌面端开发,如 QQ、微信、网易云音乐等。CEF 开源且采用 BSD 授权,商业友好,装机量已超 1 亿。此外,GitHub 项目 CefDetector 可帮助检测电脑中使用 CEF
102 3
|
2月前
|
开发者 安全 UED
JSF事件监听器:解锁动态界面的秘密武器,你真的知道如何驾驭它吗?
【8月更文挑战第31天】在构建动态用户界面时,事件监听器是实现组件间通信和响应用户操作的关键机制。JavaServer Faces (JSF) 提供了完整的事件模型,通过自定义事件监听器扩展组件行为。本文详细介绍如何在 JSF 应用中创建和使用事件监听器,提升应用的交互性和响应能力。
22 0
|
2月前
|
前端开发 开发者 Apache
揭秘Apache Wicket项目结构:如何打造Web应用的钢铁长城,告别混乱代码!
【8月更文挑战第31天】Apache Wicket凭借其组件化设计深受Java Web开发者青睐。本文详细解析了Wicket项目结构,帮助你构建可维护的大型Web应用。通过示例展示了如何使用Maven管理依赖,并组织页面、组件及业务逻辑,确保代码清晰易懂。Wicket提供的页面继承、组件重用等功能进一步增强了项目的可维护性和扩展性。掌握这些技巧,能够显著提升开发效率,构建更稳定的Web应用。
76 0
|
2月前
|
前端开发 程序员 API
从后端到前端的无缝切换:一名C#程序员如何借助Blazor技术实现全栈开发的梦想——深入解析Blazor框架下的Web应用构建之旅,附带实战代码示例与项目配置技巧揭露
【8月更文挑战第31天】本文通过详细步骤和代码示例,介绍了如何利用 Blazor 构建全栈 Web 应用。从创建新的 Blazor WebAssembly 项目开始,逐步演示了前后端分离的服务架构设计,包括 REST API 的设置及 Blazor 组件的数据展示。通过整合前后端逻辑,C# 开发者能够在统一环境中实现高效且一致的全栈开发。Blazor 的引入不仅简化了 Web 应用开发流程,还为习惯于后端开发的程序员提供了进入前端世界的桥梁。
48 0
|
2月前
|
前端开发 JavaScript 中间件
【前端状态管理之道】React Context与Redux大对决:从原理到实践全面解析状态管理框架的选择与比较,帮你找到最适合的解决方案!
【8月更文挑战第31天】本文通过电子商务网站的具体案例,详细比较了React Context与Redux两种状态管理方案的优缺点。React Context作为轻量级API,适合小规模应用和少量状态共享,实现简单快捷。Redux则适用于大型复杂应用,具备严格的状态管理规则和丰富的社区支持,但配置较为繁琐。文章提供了两种方案的具体实现代码,并从适用场景、维护成本及社区支持三方面进行对比分析,帮助开发者根据项目需求选择最佳方案。
21 0
|
20天前
|
SpringCloudAlibaba JavaScript 前端开发
谷粒商城笔记+踩坑(2)——分布式组件、前端基础,nacos+feign+gateway+ES6+vue脚手架
分布式组件、nacos注册配置中心、openfegin远程调用、网关gateway、ES6脚本语言规范、vue、elementUI
谷粒商城笔记+踩坑(2)——分布式组件、前端基础,nacos+feign+gateway+ES6+vue脚手架
|
2月前
|
存储 前端开发 JavaScript
前端语言串讲 | 青训营笔记
前端语言串讲 | 青训营笔记
29 0
下一篇
无影云桌面