`
司徒正美
  • 浏览: 13583 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

迷你MVVM框架 avalon的魔术

阅读更多

      本文将深入介绍一下avalon的运作机理及与jQuery的区别。

 

     许多人都知道MVVM是MVC的一个变体,但那些MVC是在后端的,包括微软的WPF,这意味着这个V与我们前端接触到的V差别很大。后端的V就是使用各种模板拼凑成一个静态页面给前端。而前端的V在JSer的眼中就是一个巨大的DOM树,要考虑加载时间,渲染顺序,浏览器对HTML的容错与修复,浏览器的默认事件,新图片与节点增删移动引起的reflow,是活生生的。前端的V就是一个DOM的世界,而后端就是字符串!

M,模型,一个数据体,用于填允我们的页面,通常PHP交给我们时数据已经填好了,想改变,就要再发出请求,然后通过jQuery寻找节点,修改它的innHTML与innerText。当然DOM操作是非常繁锁的,什么透明度,背景,位置等CSS属性我们都需要通过操作类名来修改。我们有时忙乎于这些细节,而把我们业务混在其中了。因此调试时,业务好了,但样式坏了,样好修好,这时务业又爆开了……

在MVVM中,M只是一个过客,它与其他表示业务状态的东西融入VM(ViewModel)中。ViewModel是一个状态的集合,当然还拖家带口监控着大量的回调。状态听起来是个深奥的概念,其实就是一个开关。比如if(aaa){}语句中的aaa,就是表示true与false,switch(bbb){}语句,它表示有多个值的状态,就像有人幼年,童年,少年,青年,中年,老年这几个阶段,它们都指向同一个东西。jQuery是什么管理状态的呢?比如我做个切换卡,点一下,这个面板显示,其他面板隐藏,它里面就使用了大量的hasClass来检测元素有没有某个表示显示的类名,有就隐藏,其他关闭。

 

$(button).click(function(){
  var index = $(this).index();
  $(panel).eq(index).show().siblings().hidden()
})

     而在MVVM中,只要妥善绑定好视图,直接像 vm.activated = 1,1 为你要显示的按钮,这样切换卡就切换过去,包括你点的按钮上的类名。你可以被动地在事件回调修改VM中的属性,也可以直接对VM中属性,无论哪一种,基本上很少看到DOM代码。

   <div ms-controller="tabs" >
        <div ms-each-tab="tabs" class="triggers">
            <a href="###" class="trigger" ms-class-selected="activated === $index" ms-click="activate">{{tab.title}}</a>
        </div>
        <div ms-each-tab="tabs" >
            <div  class="panel" ms-visible="activated === $index">{{tab.panel}}</div>
        </div >
    </div >
            avalon.ready(function() {
                avalon.define("tabs", function(a) {
                    a.tabs = [  //tabs上的title, panel来源自后端的数据库,如果PHPer帮你把整个页面搞定,这个就不需要
                        {      //只要下面的activated 与 activate 
                            title: "aaaa",
                            panel: "aaaa panel"
                        },
                        {
                            title: "bbbb",
                            panel: "ffffffffffffffffffff"
                        },
                        {
                            title: "cccc",
                            panel: "cccc panel"
                        }
                    ]
                    a.activated = 0  //这个是我们的务业产生的变量,
                    a.activate = function(){
                         a.activated = this.$scope.$index;//这是我们的业务,就是改一下activated 的值。
                    }
                })
                avalon.scan();
            })

可运行的例子见之前的博文

      在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的世界就是一个混乱的公司,全能的程序员什么都做。

 

      为了各司其职,必须有良好的分层。MVVM划分三层,M,VM,V,M是原始数据,用于转换为VM,VM管理状态与绑定回调,V通过绑定得到VM的状态与回调,渲染页面,绑定事件,切换类名,什么脏活都揽了——但用户只需要声明。avalon与其他前端MVVM框架最大的不同是,VM是用ecma262v5的新API, Object.defineProperties生成的一个充满访问器的对象,这样的对象,能通过用户对它的属性的读写,触发定义时的getter, setter函数。getter, setter对rubyer, pythoner, C#er应该很熟悉,我就不展开了。旧式IE,avalon利用VBScript的类实例,它也存在其他语言的访问器。不过,VBS对象不像JS对象那样随意添加新属性,删除已有属性,因此我们就无法监后添加的新属性。Object.defineProperties也一样,它能处理的属性也只是它定义时的属性,想监控后来的,需要再调用一次Object.defineProperties。尽管如此,也比其他MVVM框架魔幻多了。

如果监控属性不够用,可以用监控数组,计算属性与$watch, $fire方法。

比如下面一个监控数组fruits :

  var model = avalon.define("form", [], function(vm) {
                    vm.fruits = [{name: "xxx"}, {name: "yyy"}, {name: "ooo"}, {name: "ppp"}]
                    vm.numbers = [1, 2, 3, 4]
                });

    被改造成如下样子,我们可以在chrome的控制台看到它的正身:

首先这个数组的sort, splice, push, shift, reverse, pop, unshift等方法被重写,并添加了contains, ensure, remove, removeAll, removeAt, clear, upeate, set等方法, 还有表示它是个监控数组的isCollection, $id等属性。它内部四个对象也转换子ViewModel,name转换为监控属性,每个name在内部都有个set, get方法。我们只需要aaa.name = "xxx"; var d = aaa.name就能调用它们。而不像knockout那样全部转为真正的函数。

为了能互相同步,要写你绑定在页面上的字段为监控属性,如果它是一个复杂数据的某个子属性的子属性,那么至少也保持最后那个是监控属性,否则无法同步

      var model = avalon.define("test", [], function(vm) {
                    vm.fruits = [{name: "xxx"}, {name: "yyy"}, {name: "ooo"}, {name: "ppp"}]
                    vm.numbers = [1, 2, 3, 4]
                    vm.lang = "ruby"
                });
        //在这里,我们可以监听当中的name  即{{ fruits[0].name  }}, 但无法监控numbers中的元素,因为数字无法作为Object.defineProperty的第一个参数传入,因此转换不了。
        //  Object.defineProperty(object, propertyname, descriptor);//obj必须为对象 

根据这思路,整个转换过程为:

      Object.defineProperty(vm, "fruits", {set:fn, get: fn})//事实上,avalon是使用Object.defineProperties同时处理!
      Object.defineProperty(vm, "numbers ", {set:fn, get: fn})
      Object.defineProperty(vm, "lang", {set:fn, get: fn})
      Object.defineProperty(fruits[0], "name", {set:fn, get: fn})
      Object.defineProperty(fruits[1], "name", {set:fn, get: fn})
      Object.defineProperty(fruits[2], "name", {set:fn, get: fn})
      Object.defineProperty(fruits[3], "name", {set:fn, get: fn})

      比如像下面这样的复杂结构,avalon就有点为不从心了。因此不要直接把Model的数据整个扔到ViewModel中,你懒,avalon也懒——它只是监听对象属性的变化,数组长度与位置的变化。不会监听纯字符数组与纯字母数组。即便对于angular,google拥有这么多算法帝,它也要求你使用$watch来处理,而不是直接放在VM中就一了百了。emberjs, knockout就更呛了。要用好ViewModel必须对原始数据进行加工。

     var model = avalon.define("class", [], function(vm) {
                    vm.data = {
                        rows: [
                            {
                                "d": [
                                    "PO00000078",
                                    "ABC"
                                ]
                            },
                            {
                                "d": [
                                    "PO00000079",
                                    "DEF"
                                ]
                            }
                        ]
                    }
                })

 

 

       如果一定要监听,只能使用each绑定,你这个对象在层次结构上有多少个数组,就应该用多少次each绑定。比如这个对象,就有rows, d这个数组,需要用上两次。

     var model = avalon.define("class", [], function(vm) {
                    vm.data = {
                        rows: [
                            {
                                "d": [
                                    "PO00000078",
                                    "ABC"
                                ]
                            },
                            {
                                "d": [
                                    "PO00000079",
                                    "DEF"
                                ]
                            }
                        ]
                    }
                })
        <ul ms-each-row="rows" ms-controller="class">
            <li ms-each-td="row.d">
                {{td}}
            </li>
        </ul>

生成的结构如下:

      不过真正强大的是各种绑定,帮你打包所有DOM操作。可详看这里的API说明

MVVM是前端未来的发展方向,微软有knockout, winjs等MVVM框架; 著名的.Net组件开发公司 Telerik 推出了一套基于 jQuery的MVVM UI库kendoui; 谷歌组织开发angular;jQuery, rails,Sproutecore,Merb,Handlebars这几个著名框架的核心成员,超级大牛Yehuda Katz推出了emberjs!

 

       外国有文章介绍,使用了AngularJS(MVVM)代替(Backbone),代码减少了一半。

http://www.localytics.com/blog/2013/angularjs-at-localytics/

      现在园子里有不少人在用knockout与angular,在ruby china中,最爱玩新技术的rubyer最近也密集提到它们。毫无疑问,这是非常有吸引力,极具生产力的东西。

      avalon作为现在最迷你的MVVM框架,易用性也堪称一绝,绝对可以一试。

分享到:
评论

相关推荐

    基于司徒大神的轻量级MVVM框架avalon20搭建的一个简易的脚手架

    本文将深入探讨基于司徒大神创建的轻量级MVVM框架——Avalon2.0,如何搭建一个简易的脚手架。Avalon2.0是一个成熟的JavaScript框架,它为开发者提供了构建复杂前端应用的能力,尤其适用于单页应用程序(SPA)的开发...

    迷你MVVM框架---avalonjs.html

    一个迷你框架

    Android mvvm 框架,最流行的mvvm demo

    本项目“Android mvvm 框架,最流行的mvvm demo”旨在提供一个无bug的示例,帮助开发者深入理解并实践MVVM框架在Android应用中的应用。 MVVM模式源于微软的WPF开发,近年来在Android开发中逐渐流行,它通过解耦视图...

    WPF 轻量级 MVVM 框架入门 2.1.2

    **WPF轻量级MVVM框架入门2.1.2** 在Windows Presentation Foundation(WPF)开发中,MVVM(Model-View-ViewModel)模式是一种常见的设计模式,它有助于实现应用程序的视图与业务逻辑之间的解耦。MVVM通过数据绑定、...

    MVVM框架的demo

    **MVVM 框架详解** MVVM(Model-View-ViewModel)框架是现代软件开发中的一个重要概念,尤其在移动应用和Web应用开发中广泛使用。这个框架模式旨在提高应用程序的可测试性、可维护性和代码的解耦程度。在这个“MVVM...

    轻量级MVVM框架Vue.js快速上手视频教程下载

    Vue.js是一套构建用户界面的轻量级MVVM框架,与其他重量级框架不同的是, Vue.js 的核心库只关注视图层,并且非常容易学习,很容易与其它前端技术或已有的项目整合。 本系课程,主要分为两部分完成。第一部分:掌握...

    WPF中MVVM框架急速入门示例

    首先从http://mvvmlight.codeplex.com/或者http://files.cnblogs.com/chengxingliang/GalaSoft.MvvmLight.V3.rar下载它的MVVM框架下来,安装上之后,...该程序是我学习MVVM框架学习制作的小示例,很适合mvvm框架入门。

    WPF mvvm框架Stylet使用教程-窗体交互用法

    **WPF MVVM框架Stylet使用教程 - 窗体交互用法** Windows Presentation Foundation (WPF) 是Microsoft提供的一种用于构建桌面应用程序的框架,它引入了Model-View-ViewModel (MVVM) 设计模式,使得UI设计和业务逻辑...

    一步一个脚印实现一个自己的简易MVVM框架

    在IT行业中,MVVM(Model-View-ViewModel)框架是前端开发的重要组成部分,尤其是在JavaScript领域。MVVM模式是由Microsoft的WPF(Windows Presentation Foundation)引入的,后来在Web开发中得到了广泛应用,例如...

    史上最强 MVVM 框架 学习必备

    【标题】: "史上最全 MVVM 框架学习指南" 在 Android 开发领域,MVVM(Model-View-ViewModel)框架已经成为了现代应用程序设计的重要组成部分。本学习资源旨在提供全面深入的 MVVM 架构理解和实践指导,帮助开发者...

    mvvm框架整合demo

    mvvm框架整合demo 在demo内有其他的实例,和其他实用框架有利于初学都学习,也有很多的代码注释

    DevExpress MVVM框架

    DevExpress MVVM框架是DevExpress提供的一个用于支持MVVM模式的开发框架,旨在简化使用DevExpress控件的MVVM应用程序开发。MVVM模式,即Model-View-ViewModel模式,是一种软件架构设计模式,通过分离用户界面的展示...

    winform上的mvvm框架

    MVVM是一种流行的设计模式,尤其在WPF和Xamarin等框架中广泛应用,但在WinForm中相对较少见。然而,通过巧妙的实现,我们可以在WinForm上实现类似Vue的体验。 1. **MVVM模式概述**: MVVM模式分为三个主要部分:...

    winform上的mvvm框架--升级版(动态代理)

    在本文中,我们将深入探讨如何在WinForm应用中利用MVVM框架,并且通过引入Castle动态代理来提升效率和代码复用性。标题“winform上的mvvm框架--升级版(动态代理)”揭示了我们将在WinForm环境中实现一个基于MVVM...

    适用于初学者的MVVM框架

    MVVM(Model-View-ViewModel)框架是一种设计模式,尤其在现代UI开发中,如WPF(Windows Presentation Foundation)和UWP(Universal Windows Platform)应用中广泛应用。它将应用程序的界面逻辑分离开来,使开发者...

    WPF之MVVM框架实例

    WPF中的MVVM框架提供了强大的工具和结构,帮助我们构建清晰、可维护的应用程序。通过"AlarmClock"实例,我们可以看到MVVM模式如何在实践中提升开发效率和代码质量。了解并熟练运用MVVM,对于任何WPF开发者来说都是至...

    AngularjsGoogle推出的MVVM框架

    这个框架引入了一种新的概念——Model-View-ViewModel(MVVM),使得开发人员能够更加高效地处理复杂的用户界面交互,同时保持代码的可维护性和测试性。 在传统的MVVM模式中,模型(Model)代表应用程序的数据,...

    WPF MVVM开发框架

    **WPF MVVM 开发框架详解** Windows Presentation Foundation (WPF) 是 Microsoft 推出的一种用于构建桌面应用程序的用户界面框架,它集成了丰富的图形、多媒体、数据绑定和控件库,为开发者提供了强大的设计和开发...

    WPF采用MVVM框架实例

    **WPF采用MVVM框架实例** 在Windows Presentation Foundation(WPF)中,Model-View-ViewModel(MVVM)设计模式是一种广泛使用的架构模式,尤其适用于创建用户界面。它将业务逻辑、用户界面和数据绑定分离,使代码...

Global site tag (gtag.js) - Google Analytics