knockoutjs 上自己实现的flux

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

一、其实简单

flux的设计理念和实现方案,很大程度上人借鉴和参考了Vuex的实现,只是简化了某些过程,数据流向图如下:

从上图,中以看出数据的改变是单向循环的。我想这就是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。

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欢迎大家指正和提出宝贵的意见

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏FD的专栏

一步步理解python的异步IO

看到越来越多的大佬都在使用python的异步IO,协程等概念来实现高效的IO处理过程,可是我对这些概念还不太懂,就学习了一下。 因为是初学者,在理解上有很多不到...

1552
来自专栏软件开发

前端MVC Vue2学习总结(七)——ES6与Module模块化、Vue-cli脚手架搭建、开发、发布项目与综合示例

使用vue-cli可以规范项目,提高开发效率,但是使用vue-cli时需要一些ECMAScript6的知识,特别是ES6中的模块管理内容,本章先介绍ES6中的基...

1616
来自专栏葡萄城控件技术团队

程序员级别鉴定书(.NET面试问答集锦)

作为一个.NET程序员,应该知道的不仅仅是拖拽一个控件到设计时窗口中。就像一个赛车手,一定要了解他的爱车 – 能做什么不能做什么。 本文参考Scott Hans...

3757
来自专栏岑玉海

利用WCF改进文件流传输的三种方式

WCF在跨域传输使用了两种模型的方法调用:一种是同步模型,这种模型显然对那些需要大量操作时间的方法调用(如从数据库中获取大量数据时)是一种痛苦的选择。另一种是异...

3036
来自专栏Java3y

【不用框架】文件上传和下载

什么是文件上传? 文件上传就是把用户的信息保存起来。 为什么需要文件上传? 在用户注册的时候,可能需要用户提交照片。那么这张照片就应该要进行保存。 上传组件(工...

6034
来自专栏大内老A

ASP.NET MVC基于标注特性的Model验证:一个Model,多种验证规则

对于Model验证,理想的设计应该是场景驱动的,而不是Model(类型)驱动的,也就是对于同一个Model对象,在不同的使用场景中可能具有不同的验证规则。举个简...

18010
来自专栏GopherCoder

『Go 内置库第一季:error』

Go 中的错误处理和别的语言不一样,设计哲学也不一样,常有开发者埋怨 Go 语言中的错误处理。

1143
来自专栏Python自动化测试

测试驱动之xml文件的处理

Xml是可扩展标记语言,关于xml的技术本人这里不在介绍,感兴趣的同学可以去w3c看看详细的资料,这里,我仅仅介绍的是如何获取xml文档结构中的...

1423
来自专栏IMWeb前端团队

通过ffi在node.js中调用动态链接库(.so/.dll文件)

? 概述 为什么要在node.js中调用动态链接库 由于腾讯体系下的许多公共的后台服务(L5, CKV, msgQ等)已经有了非常成熟的C/C++编写的API...

5377
来自专栏林冠宏的技术文章

C++ 连接数据库的入口和获取列数、数据

这里不具体放出完整的程序,分享两个核心函数: 由于这里用到的函数是编译器自己的库所没有的,需要自己下载mysql.h库或者本地有数据库,可以去bin找到,放进去...

2538

扫码关注云+社区

领取腾讯云代金券