https://juejin.im/post/5cd50849f265da03a54c3877

在 vue 中,通信有几种形式:

  • 父子组件 emit/on
  • vuex 中共享 state
  • 跨组件 EventBus

文档中的提到的 Store 模式却鲜有人去使用讨论。笔者在研究 ElementUI的Table组件的代码组织方式,以及在自己 ElementUI 表单编辑项目中实践之后觉得其在复杂组件组织上非常有用,是一个被忽视的组件通信方法。

简单状态管理 store 模式

官方示例代码:

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 之前发生了什么。此外,每个实例/组件仍然可以拥有和管理自己的私有状态

官方版的介绍过于简陋,不妨我们更进一步,学习一下 ElementUI 的 Table 组件是如何用 Store 组织一个复杂组件的

为什么需要 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.js

模仿 Vuex 的一个好处就是我后期如果项目大了,可以十分平滑的引入 Vuex,并且如果你熟悉 Vuex,使用 Store 模式没有任何认知成本

实践

笔者用 Store 模式改造了我之前的ElementUI 的表单在线编辑器,之前的主页面由表单元素资源区,表单属性编辑区,表单元素拖拽区,表单元素属性编辑区,JSON表单生成区,代码生成区。然而整个页面就维护表单对象,表单元素列表,表单元素属性这几个值,然而这些值在多个子组件里面都起了一定的作用,一开始没有集中处理,导致数据会意外变化,不知道是那个组件引起的。后使用Store模式集中处理之后,代码逻辑清楚很多

这种方式其实就是把数据管理,数据更新的功能交给了 Store。如果你熟悉 Vuex 的话,应该很快能理解我在说什么

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

声明一个 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

复杂数据结构的父子组件的数据通信用emitv-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 模式的优点

  1. 易于调试与管理
  2. 和 EventBus 差不多的便捷,虽然做不到全局发事件,接受事件,但是如果有这种情况的话,为什么不试试 Vuex 呢
  3. 可局部应用,职责专一

EventBus 在代码量增多的情况下:

  1. 代码逻辑性极具下降,可阅读性变低
  2. 对于每一个 action 父组件都需要一个 on(或 dispatch)一个事件来处理
  3. 你将很难查找到每一个事件是从哪里触发,满篇都是业务逻辑

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 表单编辑器项目,记得点个小星星哦,查看项目地址

参考

关注下面的标签,发现更多相似文章

作者:sarva
链接:https://juejin.im/post/5cd50849f265da03a54c3877
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

为管理复杂组件状态困扰?试试 vue 简单状态管理 Store 模式【转】的更多相关文章

  1. 组件之间的通讯:vuex状态管理,state,getters,mutations,actons的简单使用(一)

    之前的文章中讲过,组件之间的通讯我们可以用$children.$parent.$refs.props.data... 但问题来了,假如项目特别大,组件之间的通讯可能会变得十分复杂... 这个时候了我们 ...

  2. 聊聊vue组件开发的“边界把握”和“状态驱动”

    vue有着完整的组件化开发机制,但是官网只给了开发的方式,对于开发规范以及组件化开发的最佳实践,还需要我们来摸索.本文就平时开发中的经验来谈谈“把握边界”和“状态驱动”这两个话题. 边界把握 边界把握 ...

  3. Vue之状态管理(vuex)与接口调用

    Vue之状态管理(vuex)与接口调用 一,介绍与需求 1.1,介绍 1,状态管理(vuex) Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态 ...

  4. 理解Vue的状态管理模式Vuex

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 状态管理模式.集中式存储管理,一听就很高大 ...

  5. Vue.js状态管理模式 Vuex

    vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 安装.使用 vuex 首先我们在 vue. ...

  6. 【Vue】状态管理

    页面应用需要Vuex管理全局/模块的状态,大型单页面组件如果靠事件(events)/属性(props)通讯传值会把各个组件耦合在一起.因 此需要Vuex统一管理,当然如是小型单页面应用,引用Vuex反 ...

  7. vue下一代状态管理Pinia.js 保证你看的明明白白!

    1.pinia的简单介绍 Pinia最初是在2019年11月左右重新设计使用Composition API的 Vue 商店外观的实验. 从那时起,最初的原则相同,但 Pinia 适用于 Vue 2 和 ...

  8. vue中状态管理vuex的使用分享

    一.main.js中引入 store import store from './store' window.HMO_APP = new Vue({ router, store, render: h = ...

  9. 每天记录一点:NetCore获得配置文件 appsettings.json vue-router页面传值及接收值 详解webpack + vue + node 打造单页面(入门篇) 30分钟手把手教你学webpack实战 vue.js+webpack模块管理及组件开发

    每天记录一点:NetCore获得配置文件 appsettings.json   用NetCore做项目如果用EF  ORM在网上有很多的配置连接字符串,读取以及使用方法 由于很多朋友用的其他ORM如S ...

随机推荐

  1. HDU - 2962 Trucking SPFA+二分

    Trucking A certain local trucking company would like to transport some goods on a cargo truck from o ...

  2. Linear Algebra - Matrix

    1. 矩阵 定义:有 \(m*n\) 个数 \(a_{ij}(i=1,2,\cdots,m; j=1,2,\cdots,n)\) 排成的 \(m\) 行 \(n\) 列的数表 \[ \begin{Bm ...

  3. DataGridView DataSource INotifyPropertyChanged 避免闪烁的方法

    代码说话: dgvPosition就是需要避免闪烁的DataGridView 主要是加2段代码 1.SetStyle 2.datagridview设置DoubleBuffered属性为True pub ...

  4. codeforces1009G Allowed Letters【贪心+hall定理】

    因为是字典序所以贪心选当前能选的最小的,所以问题就在于怎么快速计算当前这个位置能不能选枚举的字母 重排之后的序列是可以和原序列完美匹配的,而完美匹配需要满足hall定理,也就是左边任意k个集合一定和右 ...

  5. Tyvj2017清北冬令营入学测试

    P4744 A's problem(a) 时间: 1000ms / 空间: 655360KiB / Java类名: Main 背景 冬令营入学测试题,每三天结算一次成绩.参与享优惠 描述 这是一道有背 ...

  6. PJzhang:web漏洞扫描工具sitadel

    猫宁!!! 参考链接:https://www.freebuf.com/sectool/194769.html 转变博客的写作思路,力求精简快捷,不浪费自己或者他人的时间. sitadel是一款精简的w ...

  7. P5346 【XR-1】柯南家族

    题目地址:P5346 [XR-1]柯南家族 Q:官方题解会咕么? A:不会!(大雾 题解环节 首先,我们假设已经求出了 \(n\) 个人聪明程度的排名. \(op = 1\) 是可以 \(O(1)\) ...

  8. 关于setTimeout(fn,0)

    JS是单线程引擎:它把任务放到队列中,不会同步去执行,必须在完成一个任务后才开始另外一个任务. 浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:javasc ...

  9. [Java]基本数据类型及其封装类总结

    九种基本数据类型的大小,以及他们的封装类 类型 字节 默认值 封装类 byte 1 0 Byte char 2 null Character int 4 0 Integer long 8 0 Long ...

  10. 关于AQS——独占锁特性+共享锁实现(二)

    五.可中断获取锁的实现(独占锁的特性之一) 我们知道lock相较于synchronized有一些更方便的特性,比如能响应中断以及超时等待等特性,现在我们依旧采用通过学习源码的方式来看看能够响应中断是怎 ...