原文连接-https://github.com/AlloyTeam/omijavascript
先说说Store系统是干什么的!为何要造这样一个东西?可以系统架构带来什么?html
当咱们组件之间,拥有共享的数据的时候,常常须要进行组件通信。在Omi框架里,父组件传递数据给子组件很是方便:java
注:上面带有冒号的是传递javascript表达式git
经过声明onXxx="xxxx"可让子组件内执行父组件的方法。具体的以下图所示:github
若是还不明白的话,那... 我就直接上代码了:web
class Main extends Omi.Component { handlePageChange(index){ this.content.goto(index+1) this.update() } render () { return `<div> <h1>Pagination Example</h1> <Content name="content" /> <Pagination name="pagination" :data-total="100" :data-page-size="10" :data-num-edge="1" :data-num-display="4" onPageChange="handlePageChange" /> </div>`; } }
上面的例子中,数组
详细代码能够点击: 分页例子地址架构
固然你也可使用event emitter / pubsub库在组件之间通信,好比这个只有 200b 的超小库mitt 。可是须要注意mitt兼容到IE9+,Omi兼容IE8。可是,使用event emitter / pubsub库会对组件代码进行入侵,因此很是不建议在基础非业务组件使用这类代码库。框架
虽然组件通信很是方便,可是在真实的业务场景中,不只仅是父子、爷孙、爷爷和堂兄、嫂子和堂弟...
onXxx="xxxx"就显得无能为力,力不从心了,各类数据传递、组件实例互操做、 emitter/pubsub或者循环依赖,让代码很是难看且难以维护。因此:异步
Omi.Store是用来管理共享数据以及共享数据的逻辑 。
Omi Store使用足够简便,对架构入侵性极极极小(3个极表明比极小还要小)。下面一步一步从todo的例子看下Store体系怎么使用。
Omi.Store是基类,咱们能够继承Omi.Store来定义本身的Store,好比下面的TodoStore。
import Omi from 'omi' class TodoStore extends Omi.Store { constructor(data , isReady) { super(isReady) this.data = Object.assign({ items:[], length:0 },data) this.data.length = this.data.items.length } add(value){ this.data.items.push(value) this.data.length = this.data.items.length this.update() } clear(){ this.data.items.length = 0 this.data.length = 0 this.update() } } export default TodoStore
TodoStore定义了数据的基本格式和数据模型的逻辑。
好比 this.data 就是数据的基本格式:
{ items:[], length:0 }
add和clear就是共享数据相关的逻辑。
值得注意的是,在add和clear方法里都有调用this.update();这个是用来更新组件的,this.update并不会更新全部组件。可是他到底会更新哪些组件呢?等讲到store的addView方法你就明白了。
经过 new 关键字来使用TodoStore对象的实例。
let store = new TodoStore({ /* 初始化数据 */ ,/* 数据是否准备好 */ })
上面能够传入一些初始化配置信息,store里面便包含了整个应用程序共享的状态数据以及贡献数据逻辑方法(add,clear)。
固然,这些初始化配置信息多是异步拉取的。因此,有两种方法解决异步拉取store配置的问题:
为了让组件树可以使用到 store,能够经过Omi.render的第三个参数给根组件注入 store:
Omi.render(new Todo(),'body',{ store: store });
固然ES2015已经容许你这样写了:
Omi.render(new Todo(),'body',{ store });
两份代码一样的效果。
经过Omi.render注入以后,在组件树的全部组件均可以经过 this.$store 访问到 store。
为何要说beforeRender这个函数? 由于经过beforeRender转换store的data到组件的data,这样store的数据和组件的数据就解耦开了。
beforeRender是生命周期的一部分。且看下面这张图:
不论是实例化或者存在期间,在render以前,会去执行beforeRender方法。因此能够利用该方法写store的data到组件data的转换逻辑。好比:
import Omi from '../../src/index.js'; import List from './list.js'; Omi.makeHTML('List', List); class Todo extends Omi.Component { constructor(data) { super(data) } install(){ this.$store.addView(this) } beforeRender(){ this.data.length = this.$store.data.items.length } add (evt) { evt.preventDefault() let value = this.data.text this.data.text = '' this.$store.add(value) } style () { return ` h3 { color:red; } button{ color:green;} `; } clear(){ this.data.text = '' this.$store.clear() } handleChange(evt){ this.data.text = evt.target.value } render () { return `<div> <h3>TODO</h3> <button onclick="clear">Clear</button> <List name="list" data="$store.data" /> <form onsubmit="add" > <input type="text" onchange="handleChange" value="{{text}}" /> <button>Add #{{length}}</button> </form> </div>`; } } export default Todo;
为何要去写beforeRender方法?由于render只会使用this.data去渲染页面而不会去使用this.$store.data,因此须要把数据转移到组件的this.data下。这样组件既能使用自身的data,也能使用全局放this.$store.data了,不会耦合在一块儿。
注意看上面的:
install(){ this.$store.addView(this) }
经过 addView 可让 store 和 view(也就是组件的实例) 关联起来,之后store执行update方法的时候,关联的view都会自动更新!
再看上面的子组件声明:
<List name="list" data="$store.data" />
这样至关于把this.$store.data传递给了List组件。因此在List内部,就再也不须要写beforeRender方法转换了。
class List extends Omi.Component { constructor(data) { super(data) } render () { return ` <ul> {{#items}} <li>{{.}}</li> {{/items}}</ul>` } }
这里须要特别强调,不须要把全部的数据提取到store里,只提取共享数据就行了,组件自身的数据仍是放在组件本身进行管理。
一般,在真实的业务需求中,数据并非立刻可以拿到。因此这里模拟的异步拉取的todo数据:
let todoStore = new TodoStore() setTimeout(()=>{ todoStore.data.items = ["omi","store"]; todoStore.data.length = todoStore.data.items.length todoStore.beReady(); },2000)
上面的beReady就是代码已经准备就绪,在组件内部能够监听ready方法:
class Todo extends Omi.Component { constructor(data) { super(data) } install(){ this.$store.addView(this) } installed(){ this.$store.ready(()=>this.$store.update()) } add (evt) { evt.preventDefault() if(!this.$store.isReady){ return } let value = this.data.text this.data.text = '' this.$store.add(value) }
能够看到上面的add方法能够经过this.$store.isReady获取组件store是否准备就绪。