为管理复杂组件状态困扰?试试 vue 简单状态管理 Store 模式【转】
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
复杂数据结构的父子组件的数据通信用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 表单编辑器项目,记得点个小星星哦,查看项目地址
参考
作者:sarva
链接:https://juejin.im/post/5cd50849f265da03a54c3877
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
为管理复杂组件状态困扰?试试 vue 简单状态管理 Store 模式【转】的更多相关文章
- 组件之间的通讯:vuex状态管理,state,getters,mutations,actons的简单使用(一)
之前的文章中讲过,组件之间的通讯我们可以用$children.$parent.$refs.props.data... 但问题来了,假如项目特别大,组件之间的通讯可能会变得十分复杂... 这个时候了我们 ...
- 聊聊vue组件开发的“边界把握”和“状态驱动”
vue有着完整的组件化开发机制,但是官网只给了开发的方式,对于开发规范以及组件化开发的最佳实践,还需要我们来摸索.本文就平时开发中的经验来谈谈“把握边界”和“状态驱动”这两个话题. 边界把握 边界把握 ...
- Vue之状态管理(vuex)与接口调用
Vue之状态管理(vuex)与接口调用 一,介绍与需求 1.1,介绍 1,状态管理(vuex) Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态 ...
- 理解Vue的状态管理模式Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 状态管理模式.集中式存储管理,一听就很高大 ...
- Vue.js状态管理模式 Vuex
vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化. 安装.使用 vuex 首先我们在 vue. ...
- 【Vue】状态管理
页面应用需要Vuex管理全局/模块的状态,大型单页面组件如果靠事件(events)/属性(props)通讯传值会把各个组件耦合在一起.因 此需要Vuex统一管理,当然如是小型单页面应用,引用Vuex反 ...
- vue下一代状态管理Pinia.js 保证你看的明明白白!
1.pinia的简单介绍 Pinia最初是在2019年11月左右重新设计使用Composition API的 Vue 商店外观的实验. 从那时起,最初的原则相同,但 Pinia 适用于 Vue 2 和 ...
- vue中状态管理vuex的使用分享
一.main.js中引入 store import store from './store' window.HMO_APP = new Vue({ router, store, render: h = ...
- 每天记录一点:NetCore获得配置文件 appsettings.json vue-router页面传值及接收值 详解webpack + vue + node 打造单页面(入门篇) 30分钟手把手教你学webpack实战 vue.js+webpack模块管理及组件开发
每天记录一点:NetCore获得配置文件 appsettings.json 用NetCore做项目如果用EF ORM在网上有很多的配置连接字符串,读取以及使用方法 由于很多朋友用的其他ORM如S ...
随机推荐
- 牛客多校2 D-money(dp记录/贪心)
D-money 链接:https://www.nowcoder.com/acm/contest/140/D来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 131072K, ...
- Sharepoint2013搜索学习笔记之设置业务数据内容源(六)
Sharepoint搜索爬网组件支持爬Business Data Connectivity Service 承载的外部数据,关于Business Data Connectivity Service设置 ...
- [开发技巧]·TensorFlow&Keras GPU使用技巧
[开发技巧]·TensorFlow&Keras GPU使用技巧 1.问题描述 在使用TensorFlow&Keras通过GPU进行加速训练时,有时在训练一个任务的时候需要去测试结果 ...
- SpringMVC中的常用注解
RequestParam 作用: 用于 将请求参数区数据 映射到 功能处理方法的参数上. 属性: value 请求参数中的名称 required 请求参数中是否必须提供此参数. 默认值: ...
- Codeforces Round #401 (Div. 2)【A,B,C,D】
最近状态极差..水题不想写,难题咬不动..哎,CF的题那么简单,还搞崩了= =.真是巨菜无比. Codeforces777A 题意:略. 思路: 构造出3!次变换,然后输出就好. Code: #inc ...
- poj3225(线段树区间更新&hash)
题目链接: http://poj.org/problem?id=3225 题意: 初始给一个全局为 0~65536 的区间 s, 然后不断地对区间 s 进行 并, 交, 差, 相对差等运算, 输出最 ...
- Eureka 代码详解
看过之前文章的朋友们,相信已经对Eureka的运行机制已经有了一定的了解.为了更深入的理解它的运作和配置,下面我们结合源码来分别看看服务端和客户端的通信行为是如何实现的.另外写这篇文章,还有一个目的, ...
- TensorFlow高层封装:从入门到喷这本书
目录 TensorFlow高层封装:从入门到喷这本书 0. 写在前面 1. TensorFlow高层封装总览 2. Keras介绍 2.1 Keras基本用法 2.2 Keras高级用法 3. Est ...
- linux查看系统版本(适用于centos、ubutun,其他类型没有进行测试)
方法一:cat /etc/issue 或more /etc/issue root@salt-master:~# cat /etc/issueUbuntu 16.04.2 LTS \n \l 方法二:l ...
- HDU-3072-IntelligenceSystem(tarjan,贪心)
链接:https://vjudge.net/problem/HDU-3072 题意: 给你n个点,1个点到另一个点连接花费c,但是如果几个点可以相互可达,则这几个点连通花费为0. 求将整个图连通的最小 ...