原文連接-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是否準備就緒。