• 前端MVVM框架avalon揭秘


    avalon大家可能不熟悉,但是Knockout估计或多或少听过用过,那么说说KO的几个概念

    1. 监控属性(Observables)和依赖跟踪(Dependency tracking)
    2. 声明式绑定(Declarative bindings)
    3. 模板(Templating)

    本章主要提到 监控属于依赖跟踪(后改名叫计算属性)

    监控顾名思义,监听着你设定目标的变化,换句话说能够通知订阅者它的改变以及自动探测到相关的依赖。

    计算属性,就是依赖监控属性变化而自动调用处理更新

    KO的一个例子

    如果你已经有了监控属性firstName和lastName,你想显示全称怎么办? 这就需要用到依赖监控属性了 – 这些函数是一个或多个监控属性, 如果他们的依赖对象改变,他们会自动跟着改变。

    例如,下面的view model,

    var viewModel = {
        firstName: ko.observable('Bob'),
        lastName: ko.observable('Smith')
    };

    … 你可以添加一个依赖监控属性来返回姓名全称:

    viewModel.fullName = ko.dependentObservable(function () {
        return this.firstName() + " " + this.lastName();
    }, viewModel);

    并且绑定到UI的元素上,例如:

    The name is <span data-bind="text: fullName"></span>

    … 不管firstName还是lastName改变,全称fullName都会自动更新(不管谁改变,执行函数都会调用一次,不管改变成什么,他的值都会更新到UI或者其他依赖监控属性上)

    OK

    KO是怎么实现双向机制的呢?

    • 通过转换VM中所有要监听的东西为函数,然后执行它们,得到某一时刻中,一共有多少函数被执行,将它们放到栈中,最底的就是最先被执行的,它上面的就是此函数所依赖的函数,从而得到依赖关系。
    • 然后设计一个观察者模式,从上面的依赖检测中,将依赖函数作为被依赖者(最先执行的那个的)的订阅者,以后我们对被依赖者进行赋值时,就会通先订阅者更新自身,从而形成一个双向绑定链。
    • knockout会将视图中的绑定属性进行转换,分解出求值函数与视图刷新函数,视图刷新函数依赖于求值函数,而求值函数亦依赖于我们VM中的某些属性(这时,它们都转换为函数),在第一次扫描时,它们会加入对应属性的订阅者列队中, 从而VM中的某个属性改变,就会自动刷新视图、

    猪脚登场

    avalon实现双向绑定跟ko的实现其实大同小异,但是ko的实现异常的复杂,avalon则清晰很多

    上列子,然后分析

    HTML结构

    <div id='box' ms-controller="box">
        <div style=" background: #a9ea00;" ms-css-width="w" ms-css-height="h"  ms-click="click"></div>
        <p>{{ w }} x {{ h }}</p>
        <p>W: <input type="text" ms-model="w" data-event="change"/></p>
        <p>H: <input type="text" ms-model="h" /></p>
    </div>

    JS

    avalon.define("box", function(vm) {
            vm.w = 100;
            vm.h = 100;
            vm.click = function() {
                vm.w = parseFloat(vm.w) + 10;
                vm.h = parseFloat(vm.h) + 10;
            }
        })
        avalon.scan(document.getElementById('box')

    就是官网提供的一个DEMO  http://rubylouvre.github.io/mvvm/

    分析HTML结构:

    1. ms-controller="box"  作用域范围
    2. ms-css-width="w"     css样式绑定宽度
    3. ms-css-height="h"    css样式绑定高度
    4. ms-click="click"         绑定click处理事件
    5. {{ w }} x {{ h }}     插值表达式 数据填充
    6. ms-model="w"         改名叫ms-duplex 双向绑定

    JS处理中:

    1. avalon.define   构建一个view model视图模型  box就是作用域范围
    2. vm.w = 100;   定义一个监控属性宽,默认值是100
    3. vm.h = 100;   定义一个监控属性高,默认值是100
    4. vm.click          定义一个click处理方法
    5. avalon.scan     扫描节点指定绑定

    view model视图模型的构建在以前已经讲过了,这里主要讲下双向绑定的构建及处理的原理

    1.构建VM的时候,对监控属性进程转化处理,生成一个监控对象

    监控对象是通过Object.defineProperty转换过的处理函数,所以在setter,getter时候会调用转化的处理函数,这个用户是不可见的

    赋值时处理 setter

    var old = value;
    if (valueType === "array" || valueType === "object") {   //监控数组
        if (value && value.$id) {
            updateViewModel(value, neo, Array.isArray(neo))
        } else if (Array.isArray(neo)) {
            value = Collection(neo)
            value._add(neo)
        } else {
            value = modelFactory(neo, neo)
        }
    } else {
        value = neo
    }
    accessor.value = value;
    model[name] = value && value.$id ? value.$model : value;
    //值变化了,通知顶层改变
    notifySubscribers(accessor);
    vmodel.$fire && vmodel.$fire(name, value, old)

    取值时处理 getter

    collectSubscribers(accessor); //收集视图函数
         return value

    操作时

    vm.w = 100  setter,就会默认调用赋值处理函数

    vm.w  同样,getter 调用取值函数

    这样方式,比ko的this.firstName() + " " + this.lastName();  友爱多了,因为KO转换的是处理函数,必须要函数调用。。。别提多变扭

    关键点:

    collectSubscribers  //收集视图函数

    notifySubscribers //值变化了,通知顶层改变

    这个2个方法,用来处理依赖关系的

    实现的流程:

    预处理过程:

    1. 生成监控属性,其中监控属性subscribers用来收集依赖处理的回调
    2. 扫描DOM节点上的对应的属性编码 比如,ms-css-width="w"
    3. 根据css类型找到对应bindingHandlers处理句柄函数
    4. 通过parseExpr分解出求值函数
    5. 通过watchView函数生成视图更新函数
    6. 视图更新函数updateView加入到当前对应监控属性的subscribers队列中,形成依赖关系

    交互时:

    1. 用户点击某个通过ms-click="click” 绑定事件的元素(当value变化时改变model的值)视图通知模型
    2. 执行预先生成updateModel函数,通过执行’取值时处理’通知notifySubscribers(accessor)
    3. 执行accessor中subscribers的所有依赖函数,从而更新所有依赖

     画了张图。。

    本文只是很简单的说了下监控属性大概的逻辑,还有计算属性的处理,转化,调用,之后会分解

    评价:实现非常巧妙,而且代码写的清晰易懂

  • 相关阅读:
    【Charles】高级过滤
    【Python】遍历字典
    sso
    数据可视化大屏,屏幕多分辨率适配方案,且在任意屏幕下保持16:9的比例等比缩放
    从实战的角度谈微服务(八):Sentinel简单使用
    软件研发的成本,效率与质量
    Java获取POST请求Json格式参数(raw)
    Linux日志文件分析
    mongodb非交互式命令
    Java线程池之Executors.newSingleThreadExecutor()
  • 原文地址:https://www.cnblogs.com/aaronjs/p/3231462.html
Copyright © 2020-2023  润新知