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就是根状态器。
五、其他
- 当然模块化的引用,也是支持。具体实例细节可参考test中的测试示例。
- 项目的git地址:https://gitee.com/ko-plugins/flux.git,欢迎大家指正和提出宝贵的意见。
- 第二章:Shiro入门——深入浅出学Shiro细粒度权限开发框架
- 在Apache Spark上跑Logistic Regression算法
- 第四章:Shiro的身份认证(Authentication)——深入浅出学Shiro细粒度权限开发框架
- 第五章:Shiro的授权(Authorization)——深入浅出学Shiro细粒度权限开发框架
- 第六章:Shiro的Realms——深入浅出学Shiro细粒度权限开发框架
- 第八章:Shiro和Spring的集成——深入浅出学Shiro细粒度权限开发框架
- 第九章:Shiro的Web——深入浅出学Shiro细粒度权限开发框架
- 第十章:Shiro的Cache——深入浅出学Shiro细粒度权限开发框架
- Appboy基于MongoDB的数据密集型实践
- 微信企业号登录授权Java实现获取员工userid根据userid换openid
- 微信支付-微信红包Java版本
- Universe入门
- 分享一款值得分享的写作工具
- 微信二次开发Java自定义菜单事件实现
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 腾讯云主机上部署FRP+Teamviewer穿透内网进行远程运维
- 从今天起构建你的JavaScript世界
- SpringCloud开发框架入门知识
- 一张900w的数据表,怎么把原先要花费17s执行的SQL优化到300ms?
- Codeforces Round #624 (Div. 3) A - Add Odd or Subtract Even
- XMLHttpRequest
- Codeforces Round #624 (Div. 3) B - WeirdSort
- 详解 Ajax
- 这个腾讯博客是被腾讯爬虫爬过来的,样式丑,请看我博客园地址,见下文
- Codeforces Round #624 (Div. 3) C - Perform the Combo
- 都是微服务的天下了,还有不知道 JSON 的程序员吗?
- Codeforces Round #624 (Div. 3) D.Three Integers
- select 高级查询之连接查询
- select 高级查询之子查询
- Java 最实用的文件读写