在 vue 中,通讯有几种形式:vue
- 父子组件 emit/on
- vuex 中共享 state
- 跨组件 EventBus
文档中的提到的 Store 模式却鲜有人去使用讨论。笔者在研究 ElementUI的Table组件的代码组织方式,以及在本身 ElementUI 表单编辑项目中实践以后以为其在复杂组件组织上很是有用,是一个被忽视的组件通讯方法。git
简单状态管理 store 模式
官方示例代码:github
var store = { debug: true, state: { message: 'Hello!' }, setMessageAction(newValue) { if (this.debug) console.log('setMessageAction triggered with', newValue) this.state.message = newValue }, clearMessageAction() { if (this.debug) console.log('clearMessageAction triggered') this.state.message = '' } } 复制代码
官方介绍:全部 store 中 state 的改变,都放置在 store 自身的 action 中去管理。这种集中式状态管理可以被更容易地理解哪一种类型的 mutation 将会发生,以及它们是如何被触发。当错误出现时,咱们如今也会有一个 log 记录 bug 以前发生了什么。此外,每一个实例/组件仍然能够拥有和管理本身的私有状态web

官方版的介绍过于简陋,不妨咱们更进一步,学习一下 ElementUI 的 Table 组件是如何用 Store 组织一个复杂组件的vuex
为何须要 Store 模式
ElementUI 的 Table 组件,功能不少。该组件由父组件 Table.vue 和众多子组件 layout-observer,table-body,table-column,table-footer,table-header,table-layout 组成。看 ElementUI 文档就以为 Table 组件复杂。数据结构
若是把子组件的事件都 emit 到父组件处理,那么父组件得接收多少事件。而且子组件部分功能会影响父组件的布局。而且 Table 的部分数据大多数子组件都须要,你要一个一个经过 Porp 传入吗?自顶向下的数据流动开发困难。不如把这些共享的数据放在一个地方,咱们天然很容易想到 Vuex,可是 ElementUI 库引入 ElementUI 引入 Vuex,你以为合适吗,而且数据共享仅仅是在 Table 组件里面,并非全局的数据,所以采用 Store 模式再好不过了。架构
ElementUI 模仿了 Vuex 的使用方式。有兴趣的读者能够看一下 Table 组件中table-store.jsapp
模仿 Vuex 的一个好处就是我后期若是项目大了,能够十分平滑的引入 Vuex,而且若是你熟悉 Vuex,使用 Store 模式没有任何认知成本异步
实践
笔者用 Store 模式改造了我以前的ElementUI 的表单在线编辑器,以前的主页面由表单元素资源区,表单属性编辑区,表单元素拖拽区,表单元素属性编辑区,JSON表单生成区,代码生成区。然而整个页面就维护表单对象,表单元素列表,表单元素属性这几个值,然而这些值在多个子组件里面都起了必定的做用,一开始没有集中处理,致使数据会意外变化,不知道是那个组件引发的。后使用Store模式集中处理以后,代码逻辑清楚不少

经过定义和隔离状态管理中的各类概念并经过强制规则维持视图和状态间的独立性,咱们的代码将会变得更结构化且易维护

声明一个 Store 对象
const FormStore = function(form, initialState = {}) { // 将父组件的示例保存在Store里面 if (!form) { throw new Error('Form is required.') } this.form = form this.states = { ... } // initialState 里面的值必须是 this.states声明过的,这样全部状态的变化应该都在store里面能够查找,并由store控制 for (let prop in initialState) { if (initialState.hasOwnProperty(prop) && this.states.hasOwnProperty(prop)) { this.states[prop] = initialState[prop] } } } 复制代码
mutations
Vuex 中的 mutation 很是相似于事件:每一个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是咱们实际进行状态更改的地方,而且它会接受 state 做为第一个参数
咱们这里也模仿它,注意这里咱们只放同步的代码,异步代码本身处理
FormStore.prototype.mutations = {
setFormAttribute(states, formAttribute) {
this.states = { ...states, formAttribute } }, setFormItems(states, formItems) { this.states = { ...states, formItems } }, setClickedIndex(states, clickedIndex) { this.states = { ...states, clickedIndex } }, setFormItemToHandle(states, formItemToHandle) { this.states = { ...states, formItemToHandle } }, setItemInFormItems(states, idx, formItem) { states.formItems.splice(idx, 1, formItem) }, setFromItems(states, formItems) { this.states = { ...states, formItems } } } 复制代码
commit
复杂数据结构的父子组件的数据通讯用emit
和v-on
事件流容易混乱,尤为是对象嵌套对象的时候。采用 Store 模式,子组件和父组件之间有了 store 这个桥梁,经过 commit 来分发事件
在 commit 函数里面,打上一个console.log
,事件的变化所有掌握在你的手里。就像使用 Vuex 同样
// 定义 FormStore.prototype.commit = function(name, ...args) { const mutations = this.mutations console.log('emit', name) if (mutations[name]) { // states 做为第一个参数 mutations[name].apply(this, [this.states].concat(args)) } else { throw new Error(`Action not found: ${name}`) } } // 分发事件 this.store.commit('setFormItemToHandle', val) 复制代码
使用
在父组件的 data 里面建立 store,而后把 store 传入到各个子组件里面去。代码逻辑很是清楚
data() {
const store = new FormStore(this); return { store }; }, computed: { form() { return this.store.states.formAttribute; } }, methods: { genFormItem(val) { this.store.commit("setFormItemToHandle", val); } } } 复制代码
Store 模式 vs EventBus
Vuex 的优势便是 Store 模式的优势
- 易于调试与管理
- 和 EventBus 差很少的便捷,虽然作不到全局发事件,接受事件,可是若是有这种状况的话,为何不试试 Vuex 呢
- 可局部应用,职责专注
EventBus 在代码量增多的状况下:
- 代码逻辑性极具降低,可阅读性变低
- 对于每个 action 父组件都须要一个 on(或 dispatch)一个事件来处理
- 你将很难查找到每个事件是从哪里触发,满篇都是业务逻辑
Store 模式 vs Vuex
有的时候,咱们可能不知道是否该使用 Vuex,虽然 Redux 的做者 Dan Abramov 的话这么说:
Flux 架构就像眼镜:您自会知道何时须要它
可是我可能只是轻微的近视,不带眼镜也能够,可是看东西不太清楚,带上眼镜又感受有点累赘,这个时候就须要咱们的 Store 模式
Vuex 负责全局状态的管理,Store 模式负责局部状态的交流
Store 模式能够在你写一个大型组件的时候,单独在该组件中使用,不用数据都放在 Vuex 里面,做为多个子组件和父组件通讯的桥梁使用。我司后台拥有几十个业务,每一个业务下面又会有细分,若是为了写组件方便,将其放在Vuex
里面,那么Vuex
将会何等的臃肿
多人开发的时候,每一个人负责的业务有单独的 Store 也不会互相影响
你甚至可使用多个 Store 去组织你全部的代码
总结
模仿 Vuex,咱们多了一种组织复杂组件或局部状态管理的新思路,在你写复杂的组件,又不想污染全局的 Vuex,又须要将状态在多个组件中共享,则能够考虑一下 Store 模式,和 Vuex 同样方便,和 EventBus 同样轻量
既然采用了模仿 Vuex 的方式,代码风格就要贯彻到底,毕竟 Store 模式没有强力的约束,不能像 ElementUI 同样,代码里面还有直接修改 states 语句(逃
eg: this.store.states.treeData = this.getTableTreeData(value);
体验我基于 Store 模式改造的 ElementUI 表单编辑器项目,记得点个小星星哦,查看项目地址