详细分析Vuex 的应用场景
本文在 GitHub上的链接
昨天被问了一个问题:
Vuex 的应用场景有什么?什么时候适合使用 Vuex,什么时候不适合使用 Vuex?
在当时,我的答案是一般人会回答的内容:
- 涉及到非父子关系的组件,例如兄弟关系、祖孙关系,甚至更远的关系;
- 他们之间如果有数据交互,那么应该使用Vuex来实现;
- 如果页面复杂度比较低的话,也可以考虑使用
global-event-bus
来实现; - 如果只是父子关系的组件数据交互,那么应该考虑使用props进行单向传递;
- 如果涉及到子组件向父组件的数据传递,那么应该考虑使用
$emit
和$on
;
讲道理说,这个答案也不算错,当时我也只能回想起这些。
但后来,我又想了想,其实还可以更有针对性一些。
例如,在以下场景里,我们应当使用 Vuex:
1、组件会被销毁
我们可以假设这样一个场景:
- 假如有这样一个组件,他是弹窗,有一些复选框和输入框,用户会选择和填写信息;
- 然后这个弹窗会被关闭和打开,由于业务需要,这个弹窗输入的内容,希望关闭后可以保留,在重新打开后,内容依然存在。
解决办法:
- 我们可以考虑将值存在父组件中,也就是说,实际修改的是父组件的值;
- 存在比如
sessionStorage
、cookies
之类的里面,在created
时从中读取,destroyed
的时候写入其中; - 可以存到
global-event-bus
里面;
但事实上,最好的还是存在 Vuex 里:
- 可以直接通过
$store.state
来调用,通过commit()
来修改值; - 也可以在
created
的时候,读取存在 state 里面的值,在destroyed
的时候,写回 state;
这样处理的优点是解耦,不跟其他组件打交道。
2、组件基于数据而创建
我们可以假设这样一个场景:
- 用户登录后,读取权限配置表,这显然是一个异步操作;
- 这个配置表可能会影响很多页面。比如被影响的组件的加载条件,例如是
v-if="$store.state.userInfo.superVIP
;
那么:
- 因为读取权限配置表这个异步操作,可能影响多个组件,而这些组件之间的关系,显然是不可预料的(即不一定是在同一个父组件下面);
- 那么这个异步操作,写在某一个组件里就不太合适(因为其他组件读取这个组件很不方便,即使他是根组件);
解决办法:
- 一个妥协的解决办法,是写在
global-event-bus
里面来实现; - 但是显然,更好的解决办法是写在 vuex 里面更专业一些;
3、多对多事件——多处触发,影响多处
我们可以假设这样一个场景:
- 假如有一个事件,比如:切换页面显示风格,他将改变某一个变量的值;
- 当该变量为
true
时,那么页面风格为白天(主要影响v-bind:style
的值); - 当该变量为
false
时,那么页面风格为晚上(同上); - 在多个地方可以切换这个页面风格开关;
- 毫无疑问,这个变量将影响多个地方的
v-bind:style
的值; - 这就是 多对多 场景;
那么:
- 无论这个变量放在哪个组件里,其他组件调用他都是很麻烦的事情;
- 即使存于根组件,然后通过
this.$root.xx
来获取这个变量,也是很麻烦的,而且很丑陋;
解决办法:
- 如果不使用 Vuex,那么我们可能会去考虑使用
global-event-bus
来存储这个变量,并使用它; - 这不是不可以,但不优雅,而且管理麻烦;
- 而使用 Vuex,那么这就是一件很方便的事情了;
- 我们可以通过
$store.state.xxx
来获取这个变量的值; - 通过
$store.getters.yyy
来获取某些基于这个值的,表示通用样式(例如黑底白字)的对象; - 通过
$store.commit()
来提交修改(比如在某些情况下可以禁止修改); - 甚至可以通过
$store.dispatch()
来获取其他风格的样式,并通过$store.state
和$store.getters
来返回新风格的样式;
4、总结
总而言之,假如你需要 数据 和 组件 分离,分别处理,那么使用 Vuex 是非常合适的。
相反,如果不需要分离处理,那么不使用 Vuex 也没关系。
比如某个数据只跟某组件打交道,是强耦合的。那么这个数据就应该存放在该组件的 data 属性中。