zoukankan      html  css  js  c++  java
  • knockoutjs 上自己实现的flux

    在knockoutjs 上实现 Flux 单向数据流 状态机,主要解决多个组件之间对数据的耦合问题。

    一、其实简单

    flux的设计理念和实现方案,很大程度上人借鉴和参考了Vuex的实现,只是简化了某些过程,数据流向图如下:
    image
    从上图,中以看出数据的改变是单向循环的。我想这就是Flux理念的核心所在吧。Vuex中对Action规范为Action和Mutation,由action去触发Mutation,action是可以异步的,而Mutation则是同步更新。而我在设计ko的Flux时,去掉了Mutation这个环节,是因为我理解为,异步的请求一般情况下都是与api接口有关系,这块内容存在极大的变化性,应该从业务或项目构架上做一层区分。

    二、如果使用

    当然,flux只是针对knockoutjs的,所以你使用之前必须引入knockoutjs。flux主要的方法和对象

    2.1 静态方法
    方法 说明
    flux.use 在require模式下,将flux与ko做关联的方法,当然他必须先与createStore方法调用。
    flux.createStore 创建一个store(状态器)实例,当然此方法是有返回值,他的返回值可以调用register方法注册到指定的域上,但第一次调用此方法时是创建rootStore(根状态器),他不允许被注册到域的。
    2.1.1 flux.createStore参数格式
    参数名称 说明
    state 状态器相关状态数据
    actions 更改state上的状态方法,方法的第一个参数为state,第二参数开始则为传入的相关内容
    getters 获取state上的相关状态数据,当然返回是一个ko监控对象。
    2.2 实例方法

    createStore方法的执行,会在ko实例上增加$store属性,此属性是状态器的实例对象,在任何位置都可以调用他的dispatch来触发事件。

    方法 说明
    register 创建和注册一个状态域,域与域之间是相互独立存储的,域之间action或get名称是可以重复的
    unRegister 移除一个状态域
    dispatch 根据actionName调用指定的action,无返回值
    get 根据getName调用指定的get,有返回值

    三、简单的使用

    本示例定义了四个ko绑定区域,分别是:app1, app2, app3, app4。实现app4中对name的改变自动影响到app1,而app3对列表的改变自动影响到app2。 image

    3.1 定义vm并初始化store
    function ViewModel(){
        this.list = ko.observableArray();
        this.name = ko.observable('无名氏');
        this.count = ko.computed(function(){
            //必须用this,这个时候ko.$store还没创建完成,应该ko.computed创建时会执行一次此处
            //如果是子vm依赖主vm,还是可以用ko.$store的
            return this.list().length + '个数'; //需要对监控对象求值,否则computed不能有效
        },this);
    }
    
    var fullVm = new ViewModel(); 
    
    var index = 1;
    fullVm.vf={
        add: function(){
            ko.$store.dispatch('addClass',{title: 'title' + (index++)});
        }
    }
    
    var opt = {
        state: {
            class: fullVm
        },
        actions:{
            "setName":function(state, name){
                state.class.name(name);
            },
            "addClass":function(state, classInfo){
                state.class.list.push(classInfo);
            }
        },
        getters:{
            "getName":function(state){
                return state.class.name;
            }
        }
    }
    flux.createStore(opt);

    根据上述代码,首先定义了ViewModel的一个类,并创建了一个fullVm的一个实例,然后直接在fullVm实例上增加了add方法。
    opt的state引用的是fullVm,其中还配置了actions和getters相关对象,然后调用flux.createStore(opt)方法。创建一个store,并关联到ko.$store对象上。

    3.2 与视图绑定

    html代码:

    <div id="app1">
        app1:
        <span data-bind="text:ko.$store.get('getName')"></span>
    </div>
    <div id="app4">
        app4:
        <input type="text" data-bind="value:name" />
        <button type="text" data-bind="click:changeName" >改变名字</button>
        <span data-bind="text:ko.$store.state.class.name"></span>
    </div>
    <hr>
    <div id="app2">
        app2:
        <ul data-bind="foreach:list" >
            <li data-bind="text:title" ></li>
        </ul>
    </div>
    <div id="app3">
        app3: 
        <button type="button" data-bind="click:vf.add" >添加</button>
        <span data-bind="text:count"></span>
    </div>

    js代码:

    var app1 = ko.applyBindings(fullVm, document.getElementById("app1"));
    var app2 = ko.applyBindings(fullVm, document.getElementById("app2"));
    var app3 = ko.applyBindings(fullVm, document.getElementById("app3"));
    //测试两个vm之间的依赖 解藕
    var app4 = ko.applyBindings({
        name: ko.observable(),
        changeName:function(data,event){
            ko.$store.dispatch('setName', this.name());
        }
    }, document.getElementById("app4"));

    四、域的实例

    html代码:

    <div id="app1">
        <span data-bind="text:name" ></span>
    </div>
    <div id="app2">
        <span data-bind="text:name"></span>
        <span data-bind="text:full"></span>
        <button type="button" data-bind="click:changeName" >换名</button>
    </div>

    js代码:

    function rootViewModel(){
        this.name = ko.observable('root'); 
    }
    var rVM = new rootViewModel();
    flux.createStore({ state: rVM}); //创建root状态器
    
    
    var treeNode={
        name: ko.observable('node'),
        changeName:function(){
            ko.$store.areas.treeNode.state.name('新名字');
        },
        full: ko.computed(function(){
            //computed的职责:1. 监控其他对象属性的变化,而影响自身对象(flux解决);2. 合并自身对象的几个属性(在function下,有this可解)
            //不能通过ko.$store访问对象本身,因为首次对象本身还没初始化好
            var store = ko.$store;
            //(store.areas.treeNode? store.areas.treeNode.state.name() : '')  这样也是不行,因为解决第一次通不过,后面肯定不行
            return store.state.name();
        })
    }
    
    
    ko.$store.register('treeNode', flux.createStore({ state: treeNode})); //创建子状态机
    
    var app1 = ko.applyBindings(rVM, document.getElementById("app1"));
    var app2 = ko.applyBindings(treeNode, document.getElementById("app2"));

    其实很简单,首先创建rVM(也就是根状态器),然后再调用根状态器上的register方法注册一个子状态器(也就是域)。ko.$store === rVM,也就是说ko.$store就是根状态器。

    五、其他

    1. 当然模块化的引用,也是支持。具体实例细节可参考test中的测试示例。
    2. 项目的git地址:https://gitee.com/ko-plugins/flux.git欢迎大家指正和提出宝贵的意见
  • 相关阅读:
    1022词法分析实验总结
    1008词法分析
    0909对编译原理的理解
    【Windows】如何判断当前鼠标是否按下左键或右键
    【Delphi】从内存(MemoryStream)使用WMP(WindowsMediaPlayer)控件播放视频音频(Play Video with WMP from MemoryStream)
    计算机基础
    对接微信公众号
    排序与搜索
    二叉树
    3- 面向对象进阶
  • 原文地址:https://www.cnblogs.com/cqhaibin/p/7966149.html
Copyright © 2011-2022 走看看