# flux **Repository Path**: ko-plugins/flux ## Basic Information - **Project Name**: flux - **Description**: 在knockoutjs 上实现 Flux 单向数据流 状态机 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: devp - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2017-11-18 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # flux 在knockoutjs 上实现 Flux 单向数据流 状态机,主要解决多个组件之间对数据的耦合问题。 ## 其实简单 flux的设计理念和实现方案,很大程度上人借鉴和参考了Vuex的实现,只是简化了某些过程,数据流向图如下: ![image](http://ov1e3fd5m.bkt.clouddn.com/flux/flux-workflow.png) 从上图,中以看出数据的改变是单向循环的。我想这就是Flux理念的核心所在吧。Vuex中对Action规范为Action和Mutation,由action去触发Mutation,action是可以异步的,而Mutation则是同步更新。而我在设计ko的Flux时,去掉了Mutation这个环节,是因为我理解为,异步的请求一般情况下都是与api接口有关系,这块内容存在极大的变化性,应该从业务或项目构架上做一层区分。 ## 如何使用 当然,flux只是针对knockoutjs的,所以你使用之前必须引入knockoutjs。flux主要的方法和对象 ### 静态方法 方法 | 说明 --- | --- flux.use | 在require模式下,将flux与ko做关联的方法,当然他必须先与createStore方法调用。 flux.createStore | 创建一个store(状态器)实例,当然此方法是有返回值,他的返回值可以调用register方法注册到指定的域上,但第一次调用此方法时是创建rootStore(根状态器),他不允许被注册到域的。 #### flux.createStore参数格式 参数名称 | 说明 --- | --- state | 状态器相关状态数据 actions | 更改state上的状态方法,方法的第一个参数为state,第二参数开始则为传入的相关内容 getters | 获取state上的相关状态数据,当然返回是一个ko监控对象。 ### 实例方法 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](http://ov1e3fd5m.bkt.clouddn.com/flux/koflux-simple-example.gif) ### 定义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对象上。 #### 与视图绑定 html代码: ```
app1:
app4:

app2:
app3:
``` 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代码: ```
``` 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中的测试示例。 ## dispatch调用多参数的支持 ``` ko.$store.dispatch('setCurrent', data.thisFolder, self.viewName, self.viewName + ':reload' ); ``` ## 更新日志 ### 2018-01-13 * 增加computed监听多个属性的操作实例,并对监控属性的影响进行测试和控件,文件位置:test/computed.html